package com.chrisfolger.needsmoredojo.core.amd;

import com.chrisfolger.needsmoredojo.core.util.DefineStatement;
import com.intellij.lang.javascript.psi.JSElement;
import com.intellij.lang.javascript.psi.JSLiteralExpression;
import com.intellij.lang.javascript.psi.JSParameter;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;

/**
 * Meant to find the nearest AMD import (define literal + parameter) based on the user's caret position
 *
 */
public class AMDImportLocator
{
    /**
     * represents the result of searching for the nearest AMD import
     */
    public class LocatedAMDImport
    {
        private JSElement literal;
        private JSElement parameter;

        public LocatedAMDImport(JSElement literal, JSElement parameter) {
            this.literal = literal;
            this.parameter = parameter;
        }

        public JSElement getLiteral() {
            return literal;
        }

        public JSElement getParameter() {
            return parameter;
        }
    }

    protected JSElement getParameter(PsiElement elementAtCaretPosition, DefineStatement defineStatement)
    {
        if(elementAtCaretPosition == null)
        {
            return null;
        }

        if(elementAtCaretPosition.getText().equals(",") && elementAtCaretPosition.getPrevSibling() instanceof JSParameter)
        {
            return (JSElement) elementAtCaretPosition.getPrevSibling();
        }

        if(elementAtCaretPosition.getParent() instanceof JSParameter)
        {
            return (JSElement) elementAtCaretPosition.getParent();
        }

        // assume the caret element is a define literal
        int defineIndex = getIndexOfDefine(defineStatement, getDefineLiteral(elementAtCaretPosition, defineStatement));
        JSElement parameter = defineStatement.getFunction().getParameters()[defineIndex];

        return parameter;
    }

    protected JSElement getDefineLiteral(PsiElement elementAtCaretPosition, DefineStatement defineStatement)
    {
        if(elementAtCaretPosition == null)
        {
            return null;
        }

        if(elementAtCaretPosition.getParent() instanceof JSLiteralExpression)
        {
            return (JSElement) elementAtCaretPosition.getParent();
        }

        // special case
        if(elementAtCaretPosition.getText().equals(",") && elementAtCaretPosition.getPrevSibling() instanceof JSLiteralExpression)
        {
            return (JSElement) elementAtCaretPosition.getPrevSibling();
        }

        // if none of the above cases work, we assume this is a parameter and find its corresponding literal
        int parameterIndex = getIndexOfParameter(defineStatement, getParameter(elementAtCaretPosition, defineStatement));
        JSElement defineLiteral = defineStatement.getArguments().getExpressions()[parameterIndex];

        return defineLiteral;
    }

    private int getIndexOfDefine(DefineStatement defineStatement, JSElement literal)
    {
        for(int i=0; i<defineStatement.getArguments().getExpressions().length;i++)
        {
            if(defineStatement.getArguments().getExpressions()[i].getText().equals(literal.getText()))
            {
                return i;
            }
        }

        return -1;
    }

    private int getIndexOfParameter(DefineStatement defineStatement, JSElement parameter)
    {
        for(int i=0;i<defineStatement.getFunction().getParameters().length;i++)
        {
            if(defineStatement.getFunction().getParameters()[i].getText().equals(parameter.getText()))
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * Entry point for this class. Locates the AMD import define literal and parameter that the user has selected
     *
     * @param elementAtCaretPosition
     * @param file
     * @return
     */
    public LocatedAMDImport findNearestImport(PsiElement elementAtCaretPosition, PsiFile file)
    {
        DefineStatement defineStatement = new DeclareFinder().getDefineStatementItems(file);

        JSElement defineLiteral = getDefineLiteral(elementAtCaretPosition, defineStatement);
        JSElement parameter = getParameter(elementAtCaretPosition, defineStatement);

        return null;
    }
}

@@ -339,16 +339,59 @@
 PsiFile;
+%0Aimport org.jetbrains.annotations.Nullable;
 %0A%0A/**%0A *
@@ -531,16 +531,49 @@
 cator%0A%7B%0A
+    private int iterations = 0;%0A%0A
     /**%0A
@@ -1097,32 +1097,42 @@
 %0A%0A    protected 
+@Nullable 
 JSElement getPar
@@ -1204,32 +1204,58 @@
 tatement)%0A    %7B%0A
+        iterations += 1;%0A%0A
         if(eleme
@@ -1271,32 +1271,50 @@
 Position == null
+ %7C%7C iterations %3E 2
 )%0A        %7B%0A    
@@ -1783,17 +1783,23 @@
         
-i
+JSEleme
 nt defin
@@ -1803,49 +1803,10 @@
 fine
-Index = getIndexOfDefine(defineStatement,
+ =
  get
@@ -1851,32 +1851,173 @@
 defineStatement)
+;%0A        if(define == null)%0A        %7B%0A            return null;%0A        %7D%0A%0A        int defineIndex = getIndexOfDefine(defineStatement, define
 );%0A        JSEle
@@ -2141,16 +2141,26 @@
 otected 
+@Nullable 
 JSElemen
@@ -2244,32 +2244,58 @@
 tatement)%0A    %7B%0A
+        iterations += 1;%0A%0A
         if(eleme
@@ -2311,32 +2311,50 @@
 Position == null
+ %7C%7C iterations %3E 2
 )%0A        %7B%0A    
@@ -2916,17 +2916,23 @@
         
-i
+JSEleme
 nt param
@@ -2939,52 +2939,10 @@
 eter
-Index
  =
- getIndexOfParameter(defineStatement,
  get
@@ -2991,17 +2991,16 @@
 atement)
-)
 ;%0A      
@@ -3005,33 +3005,168 @@
     
-JSElement defineLiteral =
+if(parameter == null)%0A        %7B%0A            return null;%0A        %7D%0A%0A        int parameterIndex = getIndexOfParameter(defineStatement, parameter);%0A        return
  def
@@ -3230,39 +3230,8 @@
 ex%5D;
-%0A%0A        return defineLiteral;
 %0A   
@@ -4586,25 +4586,67 @@
     return n
-ull
+ew LocatedAMDImport(defineLiteral, parameter)
 ;%0A    %7D%0A%7D%0A

fix for stack overflow