/*
        This software is distributed under the "New BSD License" agreement:

        Copyright (c) 2016 Bogdan-Florin Gheran, Gabriel Cramariuc, Ionela Rusu, Elena-Gina Craciun. All rights reserved.

        Redistribution and use in source and binary forms, with or without modification, are permitted provided
        that the following conditions are met:
        - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
        - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or
        other materials provided with the distribution.
        - Neither the name of the University Stefan cel Mare of Suceava nor the names of its contributors may be used to endorse
        or promote products derived from this software without specific prior written permission.

        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
        ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
        THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Bogdan-Florin Gheran, Gabriel Cramariuc, Ionela Rusu, or Elena-Gina Craciun
        BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
        EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
        OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
        STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        SUCH DAMAGE.
 */
package com.example.circle.movingtap;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Environment;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Size;
import android.view.MotionEvent;
import android.view.View;

import java.io.File;
import java.io.FileWriter;
import java.util.Date;

public class ExperimentView extends View implements Runnable{

    private Experiment experiment = null;
    String outputFileName = "";
    boolean isTrainingMode = false;
    private Handler handler = new Handler();

    public ExperimentView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setFocusable(true);
        setFocusableInTouchMode(true);
    }

    public void startExperiment(String participantNo, boolean isTrainingMode) {
        this.isTrainingMode = isTrainingMode;
        this.outputFileName = String.format("moving%s-%s.xml", this.isTrainingMode ? "-training" : "", participantNo);
        startXML();

        Size screenSize = ((MainActivity) getContext()).getScreenSize();
        experiment = new Experiment();
        experiment.generateTrials(screenSize.getWidth(), screenSize.getHeight(), 150);

        this.resetTrialMeasurements();
        experiment.goToFirstTrial();
        handler.postDelayed(this, 20);
    }

    @Override
    public void run() {
        postInvalidate();
        handler.postDelayed(this, 20);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (experiment == null || experiment.isExperimentOver())
            return;

        Paint paint = new Paint();
        paint.setAntiAlias(true);

        // clear screen
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);
        canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);

        // display current target
        int zoneWidth = Geometry.MillimetersToPixels(100, 150);
        Rect zone = new Rect(canvas.getWidth() / 2 - zoneWidth / 2, canvas.getHeight() / 2 - zoneWidth / 2, canvas.getWidth() / 2 + zoneWidth / 2, canvas.getHeight() / 2 + zoneWidth / 2);

        Target target = experiment.getCurrentTarget();
        int x = target.getCenter().x;
        int y = target.getCenter().y;
        switch (target.getMovementDirection())
        {
            case DOWN:
                y += target.getMovementSpeed();
                if (y > zone.bottom)
                    target.setMovementDirection(Target.MovementDirection.UP);
                break;
            case UP:
                y -= target.getMovementSpeed();
                if (y < zone.top)
                    target.setMovementDirection(Target.MovementDirection.DOWN);
                break;
            case LEFT:
                x -= target.getMovementSpeed();
                if (x < zone.left)
                    target.setMovementDirection(Target.MovementDirection.RIGHT);
                break;
            case RIGHT:
                x += target.getMovementSpeed();
                if (x > zone.right)
                    target.setMovementDirection(Target.MovementDirection.LEFT);
                break;
        }
        target.setCenter(new Point(x, y));
        paint.setColor(Color.BLACK);
        canvas.drawCircle(x, y, target.getRadium(), paint);

        // display overall information about how the experiment progresses [bottom screen]
        paint.setStrokeWidth(1);
        paint.setColor(Color.GRAY);
        paint.setTextSize(10);
        canvas.drawText(String.format("%.0f%% (%d)", experiment.getProgress() * 100.0f, experiment.getTrialsCount()), experiment.getProgress() * canvas.getWidth(), canvas.getHeight(), paint);
        paint.setColor(Color.BLACK);
        canvas.drawRect(0, canvas.getHeight() - 5, experiment.getProgress() * canvas.getWidth(), canvas.getHeight(), paint);
    }

    private StringBuilder xmlData = new StringBuilder();
    private long timestampBeginTrial;   // timestamp when the current trial began
    private long timestampTouchDown;    // timestamp when the user first touched the screen in the current trial
    private ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 80);

    private void resetTrialMeasurements() {
        MainActivity.ResetMeasurements();
        timestampBeginTrial = new Date().getTime();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (experiment == null || experiment.isExperimentOver())
            return true;

        Target target = experiment.getCurrentTarget();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xmlData = new StringBuilder();
                xmlData.append(String.format("\t<Trial ID=\"%d\">\n", target.getID()));
                xmlData.append(String.format(
                        "\t\t<PreTouch aX=\"%.4f\" aY=\"%.4f\" aZ=\"%.4f\" aN=\"%.0f\" Tilt=\"%.2f\" />\n",
                        MainActivity.getAccelerationX(), MainActivity.getAccelerationY(), MainActivity.getAccelerationZ(), MainActivity.getNumberOfAccelerationMeasurements(),
                        MainActivity.getTiltZ()
                ));

                timestampTouchDown = event.getEventTime();
                for(int indexPointer = 0; indexPointer < event.getPointerCount(); indexPointer++) {
                    xmlData.append(String.format(
                            "\t\t<Touch X=\"%d\" Y=\"%d\" ID=\"%d\" P=\"%.4f\" A=\"%.4f\" O=\"%.4f\" M=\"%.4f\" m=\"%.4f\" T=\"%d\" aX=\"%.4f\" aY=\"%.4f\" aZ=\"%.4f\" aN=\"%.0f\" Tilt=\"%.2f\" TX=\"%d\" TY=\"%d\" TR=\"%d\" TZn=\"%s\" TP=\"%d\" TD=\"%s\" S=\"%d\" RS=\"%d\" />\n",
                            (int) event.getX(indexPointer), (int) event.getY(indexPointer),
                            event.getPointerId(indexPointer),
                            event.getPressure(indexPointer), event.getSize(indexPointer),
                            event.getOrientation(indexPointer), event.getTouchMajor(indexPointer), event.getTouchMinor(indexPointer),
                            event.getEventTime(),
                            MainActivity.getAccelerationX(), MainActivity.getAccelerationY(), MainActivity.getAccelerationZ(), MainActivity.getNumberOfAccelerationMeasurements(), MainActivity.getTiltZ(),
                            target.getCenter().x, target.getCenter().y, target.getRadium(), target.getZoneOnScreen(), target.getPadding(), target.getMovementDirection(), target.getMovementSpeed(), target.getSizeSpeed()
                    ));
                }
                break;

            case MotionEvent.ACTION_MOVE:
                for(int indexHistory = 0; indexHistory < event.getHistorySize(); indexHistory++)
                    for(int indexPointer = 0; indexPointer < event.getPointerCount(); indexPointer++) {
                        xmlData.append(String.format(
                                "\t\t<Touch X=\"%d\" Y=\"%d\" ID=\"%d\" P=\"%.4f\" A=\"%.4f\" O=\"%.4f\" M=\"%.4f\" m=\"%.4f\" T=\"%d\" />\n",
                                (int) event.getHistoricalX(indexPointer, indexHistory), (int) event.getHistoricalY(indexPointer, indexHistory),
                                event.getPointerId(indexPointer),
                                event.getHistoricalPressure(indexPointer, indexHistory), event.getHistoricalSize(indexPointer, indexHistory),
                                event.getHistoricalOrientation(indexPointer, indexHistory), event.getHistoricalTouchMajor(indexPointer, indexHistory), event.getHistoricalTouchMinor(indexPointer, indexHistory),
                                event.getHistoricalEventTime(indexHistory)
                        ));
                    }
                for(int indexPointer = 0; indexPointer < event.getPointerCount(); indexPointer++) {
                    xmlData.append(String.format(
                            "\t\t<Touch X=\"%d\" Y=\"%d\" ID=\"%d\" P=\"%.4f\" A=\"%.4f\" O=\"%.4f\" M=\"%.4f\" m=\"%.4f\" T=\"%d\" aX=\"%.4f\" aY=\"%.4f\" aZ=\"%.4f\" aN=\"%.0f\" Tilt=\"%.2f\" TX=\"%d\" TY=\"%d\" TR=\"%d\" TZn=\"%s\" TP=\"%d\" TD=\"%s\" S=\"%d\" RS=\"%d\" />\n",
                            (int) event.getX(indexPointer), (int) event.getY(indexPointer),
                            event.getPointerId(indexPointer),
                            event.getPressure(indexPointer), event.getSize(indexPointer),
                            event.getOrientation(indexPointer), event.getTouchMajor(indexPointer), event.getTouchMinor(indexPointer),
                            event.getEventTime(),
                            MainActivity.getAccelerationX(), MainActivity.getAccelerationY(), MainActivity.getAccelerationZ(), MainActivity.getNumberOfAccelerationMeasurements(), MainActivity.getTiltZ(),
                            target.getCenter().x, target.getCenter().y, target.getRadium(), target.getZoneOnScreen(), target.getPadding(), target.getMovementDirection(), target.getMovementSpeed(), target.getSizeSpeed()
                    ));
                }
                break;

            case MotionEvent.ACTION_UP:
                long touchTimeMs = event.getEventTime() - timestampTouchDown;
                long taskTimeMs = new Date().getTime() - timestampBeginTrial;

                for (int indexPointer = 0; indexPointer < event.getPointerCount(); indexPointer++) {
                    xmlData.append(String.format(
                            "\t\t<Touch X=\"%d\" Y=\"%d\" ID=\"%d\" P=\"%.4f\" A=\"%.4f\" O=\"%.4f\" M=\"%.4f\" m=\"%.4f\" T=\"%d\" aX=\"%.4f\" aY=\"%.4f\" aZ=\"%.4f\" aN=\"%.0f\" Tilt=\"%.2f\" TX=\"%d\" TY=\"%d\" TR=\"%d\" TZn=\"%s\" TP=\"%d\" TD=\"%s\" S=\"%d\" RS=\"%d\" />\n",
                            (int) event.getX(indexPointer), (int) event.getY(indexPointer),
                            event.getPointerId(indexPointer),
                            event.getPressure(indexPointer), event.getSize(indexPointer),
                            event.getOrientation(indexPointer), event.getTouchMajor(indexPointer), event.getTouchMinor(indexPointer),
                            event.getEventTime(),
                            MainActivity.getAccelerationX(), MainActivity.getAccelerationY(), MainActivity.getAccelerationZ(), MainActivity.getNumberOfAccelerationMeasurements(), MainActivity.getTiltZ(),
                            target.getCenter().x, target.getCenter().y, target.getRadium(), target.getZoneOnScreen(), target.getPadding(), target.getMovementDirection(), target.getMovementSpeed(), target.getSizeSpeed()
                    ));
                }

                boolean success = target.isTouchInside((int) event.getX(), (int) event.getY());
                xmlData.append(String.format(
                        "\t\t<PostTouch Offset=\"%.4f\" Success=\"%s\" TouchTime=\"%d\" TaskTime=\"%d\" />\n",
                        Geometry.EuclideanDistance((int) event.getX(), (int) event.getY(), target.getCenter().x, target.getCenter().y),
                        success ? "YES" : "NO",
                        touchTimeMs, taskTimeMs
                ));
                xmlData.append("\t</Trial>\n");
                saveToXml(xmlData.toString(), true);

                // move to next trial or repeat current trial
                resetTrialMeasurements();
                if (success) {
                    experiment.goToNextTrial();
                    toneG.startTone(ToneGenerator.TONE_DTMF_0, 100);
                }
                else {
                    toneG.startTone(ToneGenerator.TONE_CDMA_CONFIRM, 100);
                    if (!experiment.repeatCurrentTrial())
                        experiment.goToNextTrial();
                }
                postInvalidate();
                if (experiment.isExperimentOver() || (isTrainingMode && experiment.getProgress() > .10f)) {
                    ((MainActivity) getContext()).StopExperiment();
                    endXML();
                }
                break;
        }

        return true;
    }

    //region [output file]

    private void saveToXml(String text, boolean append) {
        try{
            File file = new File(Environment.getExternalStorageDirectory(), outputFileName);
            FileWriter writer = new FileWriter(file.getAbsoluteFile(), append);
            writer.append(text);
            writer.close();
        }catch(Exception exc){
        }
    }

    private void startXML()
    {
        saveToXml("<Experiment>\n", false);
    }

    private void endXML()
    {
        saveToXml("</Experiment>", true);
    }



    //endregion
}