package sk.sorien.pimpleplugin.pimple;

import com.intellij.codeInsight.completion.*;
import com.intellij.openapi.project.Project;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.PhpLanguage;
import com.jetbrains.php.lang.psi.elements.*;
import org.jetbrains.annotations.NotNull;
import sk.sorien.pimpleplugin.ProjectComponent;

/**
 * @author Stanislav Turza
 */
public class PimpleCompletionContributor extends CompletionContributor {

    public PimpleCompletionContributor() {
        // $app['<caret>']
        extend(CompletionType.BASIC, PlatformPatterns.psiElement().withLanguage(PhpLanguage.INSTANCE), new ArrayAccessCompletionProvider());
        // $app[''] = $app->extend('<caret>', ...
        extend(CompletionType.BASIC, PlatformPatterns.psiElement().withLanguage(PhpLanguage.INSTANCE), new ExtendsMethodParameterListCompletionProvider());
        // $app->register(, ['<caret>' =>])
        extend(CompletionType.BASIC, PlatformPatterns.psiElement().withLanguage(PhpLanguage.INSTANCE), new RegisterFunctionValuesCompletionProvider());
    }

    private static class ArrayAccessCompletionProvider extends CompletionProvider<CompletionParameters> {
        public void addCompletions(@NotNull CompletionParameters parameters,
                                   ProcessingContext context,
                                   @NotNull CompletionResultSet resultSet) {

            PsiElement element = parameters.getPosition().getParent();
            Project project = element.getProject();

            if(!ProjectComponent.isEnabled(project)) {
                return;
            }

            if (!(element instanceof StringLiteralExpression)) {
                return;
            }

            Container container = Utils.findContainerForPimpleArrayAccessLiteral((StringLiteralExpression) element);
            if (container == null) {
                return;
            }

            for (Service service : container.getServices().values()) {
                resultSet.addElement(new ServiceLookupElement(service, project));
            }

            for (Parameter parameter : container.getParameters().values()) {
                resultSet.addElement(new ParameterLookupElement(parameter));
            }

            for (String key : container.getContainers().keySet()) {
                resultSet.addElement(new ContainerLookupElement(key));
            }

            resultSet.stopHere();
        }
    }

    private static class ExtendsMethodParameterListCompletionProvider extends CompletionProvider<CompletionParameters> {
        public void addCompletions(@NotNull CompletionParameters parameters,
                                   ProcessingContext context,
                                   @NotNull CompletionResultSet resultSet) {

            PsiElement element = parameters.getPosition().getParent();
            Project project = element.getProject();

            if(!ProjectComponent.isEnabled(project)) {
                return;
            }

            if (!(element instanceof StringLiteralExpression)) {
                return;
            }

            Container container = Utils.findContainerForFirstParameterOfPimpleMethod((StringLiteralExpression) element);
            if (container == null){
                return;
            }

            for (Service service : container.getServices().values()) {
                resultSet.addElement(new ServiceLookupElement(service, project));
            }

            for (Parameter parameter : container.getParameters().values()) {
                resultSet.addElement(new ParameterLookupElement(parameter));
            }

            resultSet.stopHere();
        }
    }

    private static class RegisterFunctionValuesCompletionProvider extends CompletionProvider<CompletionParameters> {
        public void addCompletions(@NotNull CompletionParameters parameters,
                                   ProcessingContext context,
                                   @NotNull CompletionResultSet resultSet) {

            PsiElement stringLiteralExpression = parameters.getPosition().getParent();
            Project project = stringLiteralExpression.getProject();

            if(!ProjectComponent.isEnabled(project)) {
                return;
            }

            if (!(stringLiteralExpression instanceof StringLiteralExpression)) {
                return;
            }

            PsiElement arrayKeyElement = stringLiteralExpression.getParent();
            PsiElement element = arrayKeyElement.getParent();

             if (element instanceof ArrayHashElement) {

                 if (!arrayKeyElement.isEquivalentTo(element.getFirstChild())) {
                     return;
                 }

                element = element.getParent();
            }

            if (!(element instanceof ArrayCreationExpression)) {
                return;
            }

            PsiElement parameterList = element.getParent();
            if (!(parameterList instanceof ParameterList)) {
                return;
            }

            PsiElement[] params = ((ParameterList) parameterList).getParameters();
            if (!(params.length > 1 && params[1].isEquivalentTo(element))) {
                return;
            }

            PsiElement methodReference = parameterList.getParent();
            if (!(methodReference instanceof MethodReference)) {
                return;
            }

            String methodReferenceName = ((MethodReference) methodReference).getName();
            if ((methodReferenceName == null) || !(methodReferenceName.equals("register"))) {
                return;
            }

            Container container = Utils.findContainerForMethodReference((MethodReference) methodReference);
            if (container == null) {
                return;
            }

            for (Parameter parameter : container.getParameters().values()) {
                resultSet.addElement(new ParameterLookupElement(parameter));
            }

            resultSet.stopHere();
        }
    }
}