package com.alangpierce.lambdacalculusplayground.view;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.Toast;

import com.alangpierce.lambdacalculusplayground.ExpressionCreator;
import com.alangpierce.lambdacalculusplayground.R;
import com.alangpierce.lambdacalculusplayground.TopLevelExpressionManager;
import com.alangpierce.lambdacalculusplayground.geometry.DrawableAreaPoint;
import com.alangpierce.lambdacalculusplayground.geometry.PointConverter;
import com.alangpierce.lambdacalculusplayground.geometry.ScreenPoint;
import com.alangpierce.lambdacalculusplayground.userexpression.UserLambda;

public class ExpressionCreatorImpl implements ExpressionCreator {
    private final Context context;
    private final LayoutInflater layoutInflater;
    private final TopLevelExpressionManager expressionManager;
    private final PointConverter pointConverter;

    public ExpressionCreatorImpl(Context context, LayoutInflater layoutInflater,
            TopLevelExpressionManager expressionManager,
            PointConverter pointConverter) {
        this.context = context;
        this.layoutInflater = layoutInflater;
        this.expressionManager = expressionManager;
        this.pointConverter = pointConverter;
    }

    private static class InvalidNameException extends Exception {
        public InvalidNameException(String detailMessage) {
            super(detailMessage);
        }
    }


    @Override
    public void promptCreateDefinition() {
        View inputView = layoutInflater.inflate(R.layout.definition_name_dialog, null);
        EditText nameEditText = (EditText) inputView.findViewById(R.id.definition_name);
        AlertDialog alertDialog = new Builder(context)
                .setTitle(R.string.create_definition)
                .setView(inputView)
                .setPositiveButton(android.R.string.ok, (dialog, which) -> {
                    addDefinitionWithName(nameEditText.getText().toString());
                })
                .setNegativeButton(android.R.string.cancel, null)
                .create();
        nameEditText.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
                return true;
            }
            return false;
        });
        alertDialog.getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
        alertDialog.show();
    }

    private void addDefinitionWithName(String defName) {

        try {
            validateDefinitionName(defName);
        } catch (InvalidNameException e) {
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
            return;
        }

        // Create the view at (50dp, 50dp).
        int shiftPixels = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 50f, context.getResources().getDisplayMetrics());
        boolean alreadyOnCanvas = expressionManager.placeDefinition(
                defName, DrawableAreaPoint.create(shiftPixels, shiftPixels));
        if (alreadyOnCanvas) {
            Toast.makeText(context, "Showing existing definition.", Toast.LENGTH_SHORT).show();
        }
    }

    private void validateDefinitionName(String defName) throws InvalidNameException {
        if (defName.length() > 8) {
            throw new InvalidNameException("Definition names can only be up to 8 letters long.");
        }

        for (char c : defName.toCharArray()) {
            if (Character.isLowerCase(c)) {
                throw new InvalidNameException(
                        "Definition names can only contain capital letters and symbols.");
            }
        }
    }

    @Override
    public void promptCreateLambda() {
        View inputView = layoutInflater.inflate(R.layout.lambda_name_dialog, null);
        EditText nameEditText = (EditText) inputView.findViewById(R.id.lambda_name);
        AlertDialog alertDialog = new Builder(context)
                .setTitle(R.string.create_lambda)
                .setView(inputView)
                .setPositiveButton(android.R.string.ok, (dialog, which) -> {
                    addLambdaWithName(nameEditText.getText().toString());
                })
                .setNegativeButton(android.R.string.cancel, null)
                .create();
        nameEditText.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
                return true;
            }
            return false;
        });

        alertDialog.getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
        alertDialog.show();
    }

    private void addLambdaWithName(String varName) {
        try {
            validateVariableName(varName);
        } catch (InvalidNameException e) {
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
            return;
        }

        // Create the view at (50dp, 50dp).
        int shiftPixels = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 50f, context.getResources().getDisplayMetrics());
        DrawableAreaPoint drawableAreaPoint = DrawableAreaPoint.create(shiftPixels, shiftPixels);
        ScreenPoint screenPoint = pointConverter.toScreenPoint(drawableAreaPoint);
        UserLambda expression = UserLambda.create(varName, null);
        expressionManager.createNewExpression(
                expression, screenPoint, false /* placeAbovePalette */);
    }

    private void validateVariableName(String varName) throws InvalidNameException {
        if (varName.length() > 8) {
            throw new InvalidNameException("Variable names can only be up to 8 letters long.");
        }

        for (char c : varName.toCharArray()) {
            if (!Character.isLowerCase(c)) {
                throw new InvalidNameException(
                        "Variable names can only contain lower-case letters.");
            }
        }
    }
}

@@ -2880,17 +2880,16 @@
 Name) %7B%0A
-%0A
         
@@ -3105,341 +3105,96 @@
     
-// Create the view at (50dp, 50dp).%0A        int shiftPixels = (int) TypedValue.applyDimension(%0A                TypedValue.COMPLEX_UNIT_DIP, 50f, context.getResources().getDisplayMetrics());%0A        boolean alreadyOnCanvas = expressionManager.placeDefinition(%0A                defName, DrawableAreaPoint.create(shiftPixels, shiftPixels
+boolean alreadyOnCanvas = expressionManager.placeDefinition(defName, newExpressionPoint(
 ));%0A
@@ -5178,304 +5178,8 @@
  %7D%0A%0A
-        // Create the view at (50dp, 50dp).%0A        int shiftPixels = (int) TypedValue.applyDimension(%0A                TypedValue.COMPLEX_UNIT_DIP, 50f, context.getResources().getDisplayMetrics());%0A        DrawableAreaPoint drawableAreaPoint = DrawableAreaPoint.create(shiftPixels, shiftPixels);%0A
     
@@ -5241,25 +5241,28 @@
 int(
-drawableArea
+newExpression
 Point
+()
 );%0A 
@@ -5906,32 +5906,32 @@
 ;%0A            %7D%0A
-
         %7D%0A    %7D%0A
@@ -5929,11 +5929,705 @@
  %7D%0A    %7D
+%0A%0A    private DrawableAreaPoint newExpressionPoint() %7B%0A        // Create the view at (50dp, 50dp).%0A        int shiftPixels = (int) TypedValue.applyDimension(%0A                TypedValue.COMPLEX_UNIT_DIP, 50f, context.getResources().getDisplayMetrics());%0A%0A        // We also need to shift down to account for action bar height.%0A        TypedValue actionBarSize = new TypedValue();%0A        context.getTheme().resolveAttribute(R.attr.actionBarSize, actionBarSize, true);%0A        int actionBarHeightPixels =%0A                (int) actionBarSize.getDimension(context.getResources().getDisplayMetrics());%0A%0A        return DrawableAreaPoint.create(shiftPixels, shiftPixels + actionBarHeightPixels);%0A    %7D
 %0A%7D%0A

Fix expression creation to be in the right place