/* (non-Javadoc)
   * @see org.cfeclipse.cfml.editors.contentassist.IAssistContributor#getTagProposals(org.cfeclipse.cfml.editors.contentassist.IAssistState)
   */
  public ICompletionProposal[] getTagProposals(IAssistState state) {

    /*
     * Only show content assist if the trigger was ( or ,
     * We should probably find a better way than this, but the
     * content assist is getting in the way right now.
     */
    if (state.getTriggerData() != ',' && state.getTriggerData() != '(') {
      return null;
    }

    if (state.getTriggerData() == ' ' || state.getTriggerData() == '\t') {
      return null;
    }
    if (!checkContext(state)) return null;
    else {
      // int length = this.functionName.length();

      Set params = ((ISyntaxDictionary) this.sourceDict).getFunctionParams(this.functionName);
      String helpText = ((ISyntaxDictionary) this.sourceDict).getFunctionHelp(this.functionName);

      /*
       * here begins denny's attempt at in-page function argument proposals
       */
      if (params == null) {
        params = new LinkedHashSet();
        CFDocument doc = ((ICFDocument) state.getIDocument()).getCFDocument();
        DocItem rootItem = doc.getDocumentRoot();
        Matcher matcher;
        Pattern pattern;
        String name = "", type = "", required = "", defaultvalue = "";
        pattern =
            Pattern.compile(
                "(\\w+)[\\s=]+(((\\x22|\\x27)((?!\\4).|\\4{2})*\\4))", Pattern.CASE_INSENSITIVE);

        // nodes = rootItem.selectNodes("//function[#startpos>=0 and #endpos < 200]");
        nodes = rootItem.selectNodes("//cffunction");
        Iterator i = nodes.iterator();
        while (i.hasNext()) {
          DocItem currItem = (DocItem) i.next();

          if (currItem.getItemData().indexOf(this.functionName) > 0) {
            // Function newFunk = new Function(this.functionName);
            // System.out.println(currItem.getItemData());
            if (currItem.getFirstChild().getName().equals("cfargument")) {
              CFNodeList childNodes = currItem.getChildNodes();
              int x = 0;
              DocItem childNode = (DocItem) childNodes.get(x);
              while (childNode.getName().equals("cfargument")) {
                matcher = pattern.matcher(childNode.getItemData());
                while (matcher.find()) {
                  String value = matcher.group(2).replaceAll("'", "").replaceAll("\"", "");
                  if (matcher.group(1).toLowerCase().equals("name")) {
                    name = value;
                  }
                  if (matcher.group(1).toLowerCase().equals("type")) {
                    type = value;
                  }
                  if (matcher.group(1).toLowerCase().equals("required")) {
                    required = value;
                  }
                  if (matcher.group(1).toLowerCase().equals("default")) {
                    defaultvalue = value;
                  }
                }
                Parameter newParam =
                    new Parameter(name, type, Boolean.valueOf(required), defaultvalue);
                // Parameter newParam = new Parameter(name,type);
                params.add(newParam);
                System.out.println(currItem.getFirstChild().getItemData());
                childNode = (DocItem) nodes.get(x);
                x++;
              }
            }
          }
        }
        /*
         * here endss denny's attempt at in-page function argument proposals
         */
        if (params == null) {
          return null;
        }
      }

      Parameter[] filteredParams = getFilteredParams(params);

      int x = 0;
      String extraInfo = paramIndent + "<b>" + functionName + "</b> (\n";
      // CompletionProposal proposal = null;
      // String usage = "";
      Parameter activeParam = null;

      int paramCount = filteredParams.length;

      while (x < paramCount) {
        Parameter p = filteredParams[x];

        String delimiter = "";
        if (x + 1 < paramCount) {
          delimiter = " ,";
        }
        extraInfo += paramIndent + paramIndent;
        if (x == this.paramsSoFar) {
          activeParam = p;
          extraInfo += "<b>";
        }
        extraInfo += p.toString() + delimiter;

        if (x == this.paramsSoFar) {
          extraInfo += "</b>";
        }
        extraInfo += "\n";

        x++;
      }

      if (this.paramsSoFar == paramCount) {
        // System.out.println("End of params");
        return null;
      }

      extraInfo += paramIndent + ") \n\n";
      extraInfo += helpText;

      return getParamProposals(activeParam, extraInfo, state.getOffset(), paramCount);
    }
  }
  /* (non-Javadoc)
   * @see org.cfeclipse.cfml.editors.contentassist.IAssistContributor#getTagProposals(org.cfeclipse.cfml.editors.contentassist.IAssistState)
   */
  public ICompletionProposal[] getTagProposals(IAssistState state) {

    /*
     * Only show content assist if the trigger was ( or ,
     * We should probably find a better way than this, but the
     * content assist is getting in the way right now.
     */
    fState = state;
    if (!checkContext(state)) return null;
    else {
      // int length = this.functionName.length();

      Set params = ((ISyntaxDictionary) this.sourceDict).getFunctionParams(this.functionName);
      String helpText = ((ISyntaxDictionary) this.sourceDict).getFunctionHelp(this.functionName);
      if (params == null) {
        Set functions;
        params = new LinkedHashSet();
        CFDocument doc = ((ICFDocument) state.getIDocument()).getCFDocument();
        functions = doc.getFunctions();
        Iterator it = functions.iterator();
        while (it.hasNext()) {
          Function function = (Function) it.next();
          System.out.println(function.getName().split("\\(")[0]);
          if (function.getName().split("\\(")[0].equals(this.functionName)
              && function.getParameters() != null) {
            Iterator funkParams = function.getParameters().iterator();
            while (funkParams.hasNext()) {
              params.add((Parameter) funkParams.next());
            }
          }
        }
        if (params.size() == 0) {
          String allData = state.getDataSoFar();
          if (allData.endsWith(".") || allData.endsWith("(")) {
            allData = allData.substring(0, allData.length() - 1);
          }
          variableName = "";

          StringBuffer buf = new StringBuffer();
          for (int i = allData.length() - 1; i >= 0; i--) {
            if (!Character.isJavaIdentifierPart(allData.charAt(i))) {
              for (int j = i - 1; j >= 0; j--) {
                if (!Character.isJavaIdentifierPart(allData.charAt(j))) {
                  break;
                }
                buf.insert(0, allData.charAt(j));
              }
              break;
            }
          }
          variableName = buf.toString();
          String CFCName = AssistUtils.getCFCName(this.variableName, state);
          IFile foundCFC = AssistUtils.findCFC(CFCName);
          functions = AssistUtils.getFunctions(foundCFC, state.getOffset());
          if (functions != null) {
            it = functions.iterator();
            while (it.hasNext()) {
              Function function = (Function) it.next();
              if (function.getName().split("\\(")[0].equals(this.functionName)
                  && function.getParameters() != null) {
                Iterator funkParams = function.getParameters().iterator();
                while (funkParams.hasNext()) {
                  params.add((Parameter) funkParams.next());
                }
              }
            }
          } else {
            System.err.println("Function for " + CFCName + " are null!");
          }
        }
      }

      Parameter[] filteredParams = getFilteredParams(params);

      int x = 0;
      String extraInfo = paramIndent + "<b>" + functionName + "</b> (\n";
      // CompletionProposal proposal = null;
      // String usage = "";
      Parameter activeParam = null;

      int paramCount = filteredParams.length;

      while (x < paramCount) {
        Parameter p = filteredParams[x];

        String delimiter = "";
        if (x + 1 < paramCount) {
          delimiter = " ,";
        }
        extraInfo += paramIndent + paramIndent;
        if (x == this.paramsSoFar) {
          activeParam = p;
          extraInfo += "<b>";
        }
        if (p != null) {
          extraInfo += p.toString() + delimiter;
        }

        if (x == this.paramsSoFar) {
          extraInfo += "</b>";
        }
        extraInfo += "\n";

        x++;
      }

      if (this.paramsSoFar == paramCount || activeParam == null) {
        // System.out.println("End of params");
        return null;
      }

      extraInfo += paramIndent + ") \n\n";
      extraInfo += helpText;

      return getParamProposals(activeParam, extraInfo, state.getOffset(), paramCount);
    }
  }
  /**
   * Checks to see if the cursor is at a position in the document where function parameter info
   * should be displayed.
   *
   * @param docText
   * @param offset
   * @return
   */
  private boolean checkContext(IAssistState state) {
    this.paramsSoFar = 0;
    this.paramList = new ArrayList();
    // this.paramPositions = new HashMap();
    String docText = state.getIDocument().get();
    this.paramText = "";
    int offset = state.getOffset();
    int newOffset = offset;

    try {
      String trigger = docText.substring(offset - 1, offset);
      // System.out.println("Triggered by ["+trigger+"]");
      if (trigger.equals("#")) {
        newOffset = offset - 1;
        trigger = docText.substring(offset - 2, offset - 1);
        if (trigger.equals("'") || trigger.equals("\"")) {
          newOffset = offset - 2;
          trigger = docText.substring(offset - 3, offset - 2);
        }
      } else if (trigger.equals("'") || trigger.equals("\"")) {
        newOffset = offset - 1;
        trigger = docText.substring(offset - 2, offset - 1);
      }

      /*
       * This block checks to see if we're in a parameter already.
       * If so, it tries to figure out which one and resets the offset and trigger
       */

      if (!trigger.equals("(") && !trigger.equals(",") && !trigger.equals(")")) {
        int lastOpenParen = state.getDataSoFar().lastIndexOf("(");
        int lastComma = state.getDataSoFar().lastIndexOf(",");
        int lastCloseParen = state.getDataSoFar().lastIndexOf(")");
        if (lastOpenParen > 0 || lastComma > 0) {
          if (lastOpenParen > lastComma) {
            newOffset = state.getOffset() - state.getDataSoFar().length() + lastOpenParen + 1;
            trigger = docText.substring(newOffset - 1, newOffset);
            paramText = docText.substring(newOffset, state.getOffset());
          } else if (lastComma > lastCloseParen) {
            newOffset = state.getOffset() - state.getDataSoFar().length() + lastComma + 1;
            trigger = docText.substring(newOffset - 1, newOffset);
            paramText = docText.substring(newOffset, state.getOffset());

          } else {
            return false;
          }
          // Auto insert closing " or '
          docText.substring(state.getOffset(), state.getOffset() + 1);
          // System.out.println("Start of param text: " + paramText.substring(0,1));
          // System.out.println("End of param text " +
          // docText.substring(state.getOffset(),state.getOffset()+1));

          if (docText.length() > state.getOffset()
              && docText
                  .substring(state.getOffset(), state.getOffset() + 1)
                  .equals(paramText.substring(0, 1))) {
            paramText += paramText.substring(0, 1);
            // System.out.println("Param: " + paramText);
          }
        }
      }

      // Check if we're at the start of a function.
      if (trigger.equals("(")) {

        Pattern p = Pattern.compile("\\b\\w+\\($");
        Matcher m = p.matcher(docText.substring(0, newOffset));
        if (m.find()) {
          this.functionName = m.group().substring(0, m.group().length() - 1);
          this.paramsSoFar = 0;

          return true;
        }
      } else if (trigger.equals(",")) {
        // System.out.println("Yep");
        // Pattern p = Pattern.compile("(\\b\\w+)\\(([^,]+,)*$");
        // Matcher m = p.matcher(docText.substring(0,offset));

        boolean singleQuotesOpen, doubleQuotesOpen, functionStart;
        singleQuotesOpen = doubleQuotesOpen = functionStart = false;
        int openFunctionCount = 0;

        byte[] docBytes = docText.substring(0, newOffset).getBytes();
        int lastParamEndedAt = docBytes.length;
        String functionText = docText.substring(0, newOffset);

        for (int i = docBytes.length - 1; i >= 0; i--) {
          byte thisByte = docBytes[i];
          // System.out.println("Looking at: " + (char)thisByte);
          switch (thisByte) {
            case 34:
              if (!singleQuotesOpen) {
                doubleQuotesOpen = !doubleQuotesOpen;
                // System.out.println("double qoute found. Open? " + doubleQuotesOpen);
              }
              break;

            case 39:
              if (!doubleQuotesOpen) {
                singleQuotesOpen = !singleQuotesOpen;
                // System.out.println("single qoute found. Open? " + singleQuotesOpen);
              }
              break;

            case 40:
              if (!singleQuotesOpen && !doubleQuotesOpen) {
                openFunctionCount--;
                if (openFunctionCount < 0) {

                  String thisParam = functionText.substring(i + 1, lastParamEndedAt);
                  paramList.add(thisParam);
                  lastParamEndedAt = i;

                  functionStart = true;
                  // System.out.println("Function start found.");
                } else {
                  // System.out.println("Opening parentheses found. Open function count: " +
                  // openFunctionCount);
                }
              }
              break;

            case 41:
              if (!singleQuotesOpen && !doubleQuotesOpen) {
                openFunctionCount++;
                // System.out.println("Closing parentheses found. Open function count: " +
                // openFunctionCount);
              }
              break;

            case 44:
              if (!singleQuotesOpen && !doubleQuotesOpen && openFunctionCount < 1) {

                String thisParam = functionText.substring(i + 1, lastParamEndedAt);
                paramList.add(thisParam);
                lastParamEndedAt = i;
                this.paramsSoFar++;
                // System.out.println("Comma found. Params so far " + this.paramsSoFar);
              }
              break;

              // default: System.out.println("Found " + (char)thisByte + ":" + (int)thisByte);
          }
          if (functionStart) {
            offset = i + 1;
            // System.out.println("Looking in " + docText.substring(0,offset));
            Pattern p = Pattern.compile("\\b\\w+\\($");
            Matcher m = p.matcher(docText.substring(0, offset));
            if (m.find()) {
              // System.out.println("Found " + m.group());
              this.functionName = m.group().substring(0, m.group().length() - 1);
              // System.out.println(this.functionName);
              return true;
            }
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }
  private ICompletionProposal[] getValueProposals(
      Parameter activeParam, String extraInfo, int offset, int paramCount) {
    // String value = "";
    String docText = fState.getDataSoFar();
    char strDelim = (docText.lastIndexOf("'") > docText.lastIndexOf("\"")) ? '\'' : '\"';
    String suffix = ",";
    Set values = activeParam.getValues();
    ICompletionProposal[] tmpResult = new ICompletionProposal[values.size()];

    if (this.paramsSoFar == paramCount - 1) {
      suffix = "";
    }

    Iterator i = values.iterator();

    Pattern pattern = Pattern.compile("([\"']?)([^\"']*)([\"']?)");
    Matcher matcher = pattern.matcher(paramText);

    String cleanParamText = "";

    if (matcher.find()) {
      cleanParamText = matcher.group(2);
      if (cleanParamText.equals("\"") || cleanParamText.equals("'")) {
        cleanParamText = "";
      }
    }

    int x = 0;
    while (i.hasNext()) {

      Object o = i.next();
      if (o instanceof Value) {
        Value val = (Value) o;
        boolean wee = val.toString().matches("^\"");
        if (cleanParamText.length() == 0
            || val.toString().toLowerCase().startsWith(cleanParamText.toLowerCase())) {
          String insertion =
              val.toString().substring(cleanParamText.length(), val.toString().length());
          if (activeParam.getType().equalsIgnoreCase("string") && !insertion.matches("^[\"|'].*")) {
            insertion = strDelim + insertion + strDelim;
          }
          int cursorOffset = insertion.length() + suffix.length();

          if (!paramText.endsWith("\"") && paramText.startsWith("\"")) {
            insertion += "\"";
            cursorOffset++;
          } else if (paramText.startsWith("\"")) {
            cursorOffset++;
          }
          if (!paramText.endsWith("'") && paramText.startsWith("'")) {
            insertion += "'";
            ;
            cursorOffset++;
          } else if (paramText.startsWith("'")) {
            cursorOffset++;
          }

          CompletionProposal proposal =
              new CompletionProposal(
                  insertion + suffix,
                  offset,
                  0,
                  cursorOffset,
                  CFPluginImages.get(CFPluginImages.ICON_PARAM),
                  activeParam.toString() + " - " + insertion,
                  null,
                  extraInfo);

          // System.out.println("Added " + val.toString());
          tmpResult[x] = proposal;
          x++;
        }
      }
    }

    ICompletionProposal[] result = new ICompletionProposal[x];
    // System.out.println("Temp array length: " + x);
    for (int y = 0; y < x; y++) {
      result[y] = tmpResult[y];
    }
    return result;
  }