package com.chrisfolger.needsmoredojo.core.refactoring;

import com.chrisfolger.needsmoredojo.core.amd.DeclareFinder;
import com.chrisfolger.needsmoredojo.core.util.JSUtil;
import com.intellij.lang.javascript.psi.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.psi.PsiElement;

public class UtilConverter implements DeclareFinder.CompletionCallback
{
    @Override
    public void run(Object[] result)
    {
        JSCallExpression expression = (JSCallExpression) result[0];
        final JSReturnStatement returnStatement = (JSReturnStatement) result[1];

        // this will be used to determine what we mixin to the util
        JSArrayLiteralExpression arrayLiteral = (JSArrayLiteralExpression) expression.getArguments()[0];
        final JSExpression[] expressionsToMixin = arrayLiteral.getExpressions();

        // now we need to get the object literal with all of the function names
        JSObjectLiteralExpression literal = (JSObjectLiteralExpression) expression.getArguments()[1];
        final JSProperty[] methodsToConvert = literal.getProperties();

        CommandProcessor.getInstance().executeCommand(expression.getProject(), new Runnable() {
            @Override
            public void run() {
                ApplicationManager.getApplication().runWriteAction(new Runnable() {
                    @Override
                    public void run() {
                        doRefactor(returnStatement, expressionsToMixin, methodsToConvert);
                    }
                });
            }
        },
        "Convert class module to util module",
        "Convert class module to util module");
    }

    public void doRefactor(JSReturnStatement originalReturnStatement, JSExpression[] mixins, JSProperty[] properties) {
        // insert new items before the return statement
        PsiElement parent = originalReturnStatement.getParent();

        // build an array of mixins for the new declare statement
        StringBuilder mixinArray = new StringBuilder();
        for (JSExpression mixin : mixins) {
            if (mixinArray.toString().equals("")) {
                mixinArray.append(mixin.getText());
            } else {
                mixinArray.append(", " + mixin.getText());
            }
        }

        // create the declare statement and add it before the return statement
        String declareStatement = String.format("var util = declare([%s], {});", mixinArray.toString());
        JSUtil.addStatementBeforeElement(parent, originalReturnStatement, declareStatement);

        // convert each property to an assignment statement
        for (JSProperty property : properties) {
            String propertyStatement = String.format("util.%s = %s;", property.getName(), property.getValue().getText());
            JSUtil.addStatementBeforeElement(parent, originalReturnStatement, propertyStatement);
        }

        // add the final statement to return the util
        String newReturnStatement = "return util;";
        JSUtil.addStatementBeforeElement(parent, originalReturnStatement, newReturnStatement);

        // delete the old return declare(...) block
        originalReturnStatement.delete();
    }
}

@@ -441,37 +441,861 @@
     
-@Override%0A    public void run
+public class UtilItem%0A    %7B%0A        private JSExpression%5B%5D expressionsToMixin;%0A        private JSProperty%5B%5D methodsToConvert;%0A        private JSReturnStatement returnStatement;%0A%0A        public UtilItem(JSExpression%5B%5D expressionsToMixin, JSProperty%5B%5D methodsToConvert, JSReturnStatement returnStatement) %7B%0A            this.expressionsToMixin = expressionsToMixin;%0A            this.methodsToConvert = methodsToConvert;%0A            this.returnStatement = returnStatement;%0A        %7D%0A%0A        public JSReturnStatement getReturnStatement() %7B%0A            return returnStatement;%0A        %7D%0A%0A        public JSExpression%5B%5D getExpressionsToMixin() %7B%0A            return expressionsToMixin;%0A        %7D%0A%0A        public JSProperty%5B%5D getMethodsToConvert() %7B%0A            return methodsToConvert;%0A        %7D%0A    %7D%0A%0A    public UtilItem getDeclareStatementFromParsedStatement
 (Obj
@@ -1969,24 +1969,254 @@
 perties();%0A%0A
+        return new UtilItem(expressionsToMixin, methodsToConvert, returnStatement);%0A    %7D%0A%0A    @Override%0A    public void run(Object%5B%5D result)%0A    %7B%0A        final UtilItem utilItem = getDeclareStatementFromParsedStatement(result);%0A
         Comm
@@ -2257,26 +2257,45 @@
 Command(
-expression
+utilItem.getReturnStatement()
 .getProj
@@ -2561,17 +2561,29 @@
 efactor(
-r
+utilItem.getR
 eturnSta
@@ -2592,31 +2592,59 @@
 ment
-, expressionsToMixin, m
+(), utilItem.getExpressionsToMixin(), utilItem.getM
 etho
@@ -2654,16 +2654,18 @@
 oConvert
+()
 );%0A     

refactor utilconverter logic