package com.visions.ebread.presenter.show_document;

import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.Typeface;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.util.ArrayMap;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.InputType;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.visions.ebread.presenter.manager.DocumentManager;
import com.visions.ebread.model.documents.Document;
import com.visions.ebread.R;
import com.visions.ebread.presenter.manager.SettingManager;
import com.visions.ebread.model.tts_interface.InterfaceFATTS;
import com.visions.ebread.model.tts_interface.Synth;
import com.visions.ebread.model.tts_interface.VolleyRequest;
import com.visions.ebread.eBreadApplication;

import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;

import static java.lang.Thread.sleep;

/**
 * A simple {@link Fragment} subclass.
 * Use the {@link ShowDocumentFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class ShowDocumentFragment extends Fragment  implements Synth.SynthCallback {

    private static final String CURRENT_DOCUMENT = "CURRENT_DOCUMENT";

    private Document currentDocument;
    SettingManager settingManager;
    private ArrayMap<String, String> palette = new ArrayMap<String, String>();
    private ArrayList<String> pages = new ArrayList<String>();
    ArrayList<Integer> pageStart = new ArrayList<Integer>();
    private TextView textView;
    private String content;
    private SpannableString currentContent;
    private ArrayMap<String, Integer> currentValues = new ArrayMap<>();
    private float oldTouchValue;
    long start = 0;
    private LinkedList<byte[]> bufferAudio = new LinkedList<>();
    private LinkedList<ArrayList<Integer>> bufferSyncData = new LinkedList<>();
    private ArrayList<Integer> currentSyncData;
    private Object underline;
    private Thread getTimeThread = null;
    private MediaPlayer audioPlayer;
    private ArrayMap<String, Boolean> boolValues = new ArrayMap<>();
    private volatile Boolean nextText = false;
    private volatile Boolean detached = false;

    @Inject VolleyRequest requestQueue;
    @Inject DocumentManager documentManager;
    private Synth synth;

    /**
     * Empty default constructor
     */
    public ShowDocumentFragment() {
        // Required empty public constructor
    }

    /**
     * Factory method that returns a new instance of ShowDocumentFragment. It puts the document
     * object as Bundle object
     * @param document  Document object
     * @return          ShowDocumentFragment instance
     */
    public static ShowDocumentFragment newInstance(Document document) {
        ShowDocumentFragment fragment = new ShowDocumentFragment();
        Bundle args = new Bundle();
        args.putSerializable(CURRENT_DOCUMENT, document);
        fragment.setArguments(args);
        return fragment;
    }

    /**
     * onCreate method that sets the currentDocument object by using Bundle arguments
     * @param savedInstanceState used for restoring to previous state
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            currentDocument = (Document)getArguments().getSerializable(CURRENT_DOCUMENT);
        }
        ((eBreadApplication) getActivity().getApplication())
                .getComponent()
                .inject(this);
        synth = new InterfaceFATTS(requestQueue, this);
        requestQueue.setRequestQueueContext(getContext());
        settingManager=new SettingManager();
        boolValues.put("waiting",false);
        pages.clear();
        currentValues.put("currentPage", 0);

        resetValues();
    }


    @Override
    public void onStart() {
        super.onStart();
    }

    /**
     *
     * @param inflater
     * @param container
     * @param savedInstanceState
     * @return
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_show_document, container, false);

        int size;
        int lineSpacing;
        String font;
        int maxLines = 6;

        // Get settings from settingManager
        try{ settingManager.loadSettings(getContext());}
        catch (IOException i) {}
        catch (XmlPullParserException x) {}

        try {
            palette = settingManager.getPalette(getContext(), "current");
        } catch (IOException i) {}

        String highlight;
        highlight =  settingManager.getHighlight();
        if (highlight.equals("underline")) {
            underline = new UnderlineSpan();
        } else {
            underline = new BackgroundColorSpan(getResources().getColor(R.color.highlight));
        }

        ArrayMap<String, String> settings = settingManager.getSettings();
        size = Integer.parseInt(settings.get("size"));
        lineSpacing = Integer.parseInt(settings.get("lineSpacing"));
        font = settings.get("font");

        try {
            System.out.println("Get content");
            content = currentDocument.getDocumentContent();
        } catch (IOException f){}


        // Apply settings to textView
        textView = (TextView) view.findViewById(R.id.textView);
        textView.setTextSize(size);
        textView.setLineSpacing(1, lineSpacing);

        Typeface type;
        try {
            type = Typeface.createFromFile("/system/fonts/" + settingManager.getSettings().get("font") + ".ttf");
        }catch (Exception e){
            type = Typeface.createFromAsset(getActivity().getAssets(), SettingManager.getFont(font, Integer.valueOf(settingManager.getSettings().get("spacing"))));
        }
        textView.setTypeface(type);

        String color = palette.get("background");
        if (color != null) {
            view.setBackgroundColor(Color.parseColor(color));
        }

        // Listener: swipe motion on the textview to change page; longpress to create bookmark
        textView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN: {
                        oldTouchValue = event.getX();
                        start = System.currentTimeMillis();
                        return true;
                    }
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_POINTER_UP:
                    case MotionEvent.ACTION_CANCEL: {
                        float newTouchValue = event.getX();
                        long end = System.currentTimeMillis();
                        if (end-start > 1000){
                            int offset = getOffsetForPosition(textView, event.getX(), event.getY());
                            offset = getWordStart(currentContent.toString(), offset);
                            onTextViewPositionSelected(pageStart.get(currentValues.get("currentPage")) + offset);
                        }
                        else {
                            if (oldTouchValue + 100 < newTouchValue || oldTouchValue - 100 > newTouchValue) {
                                if (oldTouchValue + 100 < newTouchValue) prevPage();
                                if (oldTouchValue - 100 > newTouchValue) nextPage();
                            } else {
                                if (oldTouchValue + 10 > newTouchValue || oldTouchValue - 10 < newTouchValue) {
                                    int offset = getOffsetForPosition(textView, event.getX(), event.getY());
                                    if ((audioPlayer == null || !audioPlayer.isPlaying()) && !boolValues.get("waiting")) {
                                        //Usare dialogStartReadingFrom per avere un dialog di conferma per l'avvio della lettura
                                        //dialogStartReadingFrom(offset);
                                        startReadingFrom(offset);
                                    } else {
                                        pause();
                                    }
                                }
                            }
                        }
                        return true;
                    }
                }
                return false; // return true o return false??
            }
        });

        // Set textView palette and text

        SetTextAtStartup setTextAtStartup = new SetTextAtStartup();
        Thread textViewThread = new Thread(setTextAtStartup);
        textViewThread.start();


        // SET UP THE TOOLBAR
        Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbarSD);

        if (toolbar != null) {
            ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
            toolbar.setTitle(R.string.app_name);
            setHasOptionsMenu(true);
        }


        return view;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.clear();
        //inflater.inflate(R.menu.show_document_menu, menu);
        inflater.inflate(R.menu.show_document_menu, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onPause() {
        super.onPause();
        saveCurrentPosition();
        detached = true;
        if (getTimeThread != null && getTimeThread.isAlive()) { getTimeThread.interrupt(); }
        if (audioPlayer != null && audioPlayer.isPlaying()) {
            pause();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        detached = false;
    }

    /**
     * method invoked from OnTouchListener in order to get the character offset on which the user long-pressed inside the page.
     * @param textView  fragment textView
     * @param x         x coordinate of the touch event
     * @param y         y coordinate of the touch event
     * @return          character offset
     */
    public int getOffsetForPosition(TextView textView, float x, float y) {
        if (textView.getLayout() == null) {
            return -1;
        }
        final int line = getLineAtCoordinate(textView, y);
        final int offset = getOffsetAtCoordinate(textView, line, x);
        return offset;
    }

    /**
     * method invoked from getOffsetForPosition() in order to get the line number on which the user long-pressed
     * @param textView2 fragment textView
     * @param y         y coordinate of the touch event
     * @return          line number
     */
    private int getLineAtCoordinate(TextView textView2, float y) {
        y -= textView2.getTotalPaddingTop();
        y = Math.max(0.0f, y);
        y = Math.min(textView2.getHeight() - textView2.getTotalPaddingBottom() - 1, y);
        y += textView2.getScrollY();
        return textView2.getLayout().getLineForVertical((int) y);
    }

    /**
     * method invoked from getOffsetForPosition() in order to get the character offset
     * @param textView2 fragment textView
     * @param line      line number
     * @param x         x coordinate of the touch event
     * @return          offset
     */
    private int getOffsetAtCoordinate(TextView textView2, int line, float x) {
        x = convertToLocalHorizontalCoordinate(textView2, x);
        return textView2.getLayout().getOffsetForHorizontal(line, x);
    }

    /**
     * method that transform an absolute x coordinate to a relative x coordinate
     * @param textView2 fragment textView
     * @param x         x coordinate of the touch event
     * @return          relative x coordinate
     */
    private float convertToLocalHorizontalCoordinate(TextView textView2, float x) {
        x -= textView2.getTotalPaddingLeft();
        x = Math.max(0.0f, x);
        x = Math.min(textView2.getWidth() - textView2.getTotalPaddingRight() - 1, x);
        x += textView2.getScrollX();
        return x;
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        saveCurrentPosition();
        if (audioPlayer != null) {
            audioPlayer.release();
            audioPlayer = null;
        }
    }

    /**
     * the method identifies the word the user clicked on and then it creates a dialog in order to
     * understand whether the user wants to start the audio from that word or not
     * @param start string offset, start of the clicked word
     */
    private void dialogStartReadingFrom(final int start){
        if ((audioPlayer == null || !audioPlayer.isPlaying()) && !boolValues.get("waiting")) {

            int wordStart = 0;
            String word = content.substring(start+pageStart.get(currentValues.get("currentPage")));
            int end;
            Pattern pattern = Pattern.compile("(\\w*'?)*\\w");
            Matcher matcher = pattern.matcher(word);
            if(matcher.find()) {
                wordStart = matcher.start();
                end = matcher.end();
            } else {
                end = word.length();
            }

            word = word.substring(wordStart,end);


            final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
            alertDialogBuilder.setMessage("Vuoi cominciare la riproduzione da '" + word + "'?");
            alertDialogBuilder.setNegativeButton("No",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int arg1) {
                            dialog.dismiss();
                        }
                    });
            alertDialogBuilder.setPositiveButton("Sì",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int arg1) {
                            Log.d("start",String.valueOf(start));
                            startReadingFrom(start);
                        }
                    });

            AlertDialog alertDialog = alertDialogBuilder.create();
            alertDialog.show();

        }
    }

    /**
     *  Set up the pages filling them with the selected document when the textView is ready
     */
    private class SetTextAtStartup implements Runnable {
        public void run() {
            while (textView.getLayout() == null) {
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    int maxLines = (int)  ((textView.getHeight() - textView.getPaddingTop() - textView.getPaddingBottom()) / (textView.getLineHeight() + textView.getLineSpacingExtra()));
                    textView.setMaxLines(maxLines);

                    if (pages.isEmpty()) {
                        textView.setText(content);
                        createPages(content, textView, maxLines);
                    }

                    textView.setEllipsize(TextUtils.TruncateAt.END);

                    //If i selected a bookmark i reset the data to start at the new position otherwise i continue where i was
                    if ( currentValues.get("currentSentence") != currentDocument.getLastReadPosition() ) {
                        resetValues();
                        currentValues.put("currentSentence", currentDocument.getLastReadPosition());
                        if (audioPlayer != null) { audioPlayer.reset(); }
                    }

                    //Reopen the text at the position i was before
                    int currentReadText = 0;
                    if (currentSyncData != null) { currentReadText = currentSyncData.get(currentValues.get("currentPosition")+1); }
                    currentValues.put("currentPage", fromIndexToPage(currentDocument.getLastReadPosition()+currentReadText));
                    applyThemeTextView(pages.get(currentValues.get("currentPage")));
                    setToolbarPage();
                }
            });
        }
    }

    /**
     * method that splits the document content in pages that fit the user's screen according to his settings preferences
     * @param textView  fragment textView
     * @param content   document content
     * @param maxLines  maxLines in a page
     */
    private void createPages(String content, TextView textView, int maxLines){
        boolean flag = true;
        int start, end;
        for (int i = 0; flag; i = i + maxLines) {
            start = textView.getLayout().getLineStart(i);
            if(textView.getLineCount() > i + maxLines - 1){
                end = textView.getLayout().getLineEnd(i + maxLines - 1);
            }
            else{
                flag = false;
                end = textView.getLayout().getLineEnd(textView.getLineCount() - 1);
            }
            String str;
            if(flag) str = content.substring(start, end);
            else str = content.substring(start);

            pages.add(str);
            pageStart.add(start);
        }
    }


    /**
     * method that applies the theme, using datas contained in palette, to the document content
     * return the SpannableString in case it's needed elsewhere in the calling object
     * @param tempTextView  fragment textView
     * @param content   document content
     * @param palette   current palette
     */
    public static SpannableString applyTheme(TextView tempTextView, String content, ArrayMap<String, String> palette){
        String color = palette.get("everything");
        SpannableString spannableContent;
        if (color != null) {
            spannableContent = new SpannableString(content);
            tempTextView.setTextColor(Color.parseColor(color));
        }
        else {
            spannableContent = new SpannableString(content);
            int flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
            for (int index = 0; index < content.length(); index++) {
                String ch = content.substring(index, index + 1);
                color = palette.get(ch.toLowerCase());
                if (color != null) {
                    spannableContent.setSpan(new ForegroundColorSpan(Color.parseColor(color)), index, index+1, flag);
                }
            }
        }
        tempTextView.setText(spannableContent);
        return spannableContent;
    }

    /**
     * Get a string and call the method to apply the correct colors.
     * Then save the SpannableString that has been used to be used later from the Fragment
     * @param content contain the text that is going to be displayed on the screen
     */
    private void applyThemeTextView(String content) {
        currentContent = applyTheme(textView, content, palette);
    }

    /**
     * Receiver invoked when a swipe left is performed. If possibile, it shows the next page
     *
     */
    private void nextPage(){
        if (currentValues.get("currentPage") < pages.size() - 1) {
            currentValues.put("currentPage", currentValues.get("currentPage")+1);
            //currentPage++;
            //Toast.makeText(getActivity(), ((currentPage + 1) + "/" + pages.size()), Toast.LENGTH_SHORT).show();
            applyThemeTextView(pages.get(currentValues.get("currentPage")));
            if (!boolValues.get("pageEnded")){
                pause();
            }
            setToolbarPage();
        }
        else{
            Toast.makeText(getActivity(), "Fine del documento", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Receiver invoked when a swipe right is performed. If possibile, it shows the previous page
     *
     */
    private void prevPage(){
        if (!pages.isEmpty() && currentValues.get("currentPage") > 0) {
            currentValues.put("currentPage", currentValues.get("currentPage")-1);
            //currentPage--;
            //Toast.makeText(getActivity(), ((currentPage + 1) + "/" + pages.size()), Toast.LENGTH_SHORT).show();
            applyThemeTextView(pages.get(currentValues.get("currentPage")));
            pause();
            setToolbarPage();
        }
        else{
            Toast.makeText(getActivity(), "Inizio del documento", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Method to load the selected page in the textView on screen
     * @param page The number of the page at which we want to go
     */
    private void goToPage(int page){
        if (page < pages.size()) {
            currentValues.put("currentPage", page);
            //currentPage=page;
            Toast.makeText(getActivity(), (currentValues.get("currentPage")+1 + "/" + pages.size()), Toast.LENGTH_SHORT).show();
            applyThemeTextView(pages.get(currentValues.get("currentPage")));
            setToolbarPage();
        } else {
            Toast.makeText(getActivity(), (R.string.page_not_found), Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * This method look for the page corresponding to an index inside the text
     * This is used to look for the correct page to load when using a bookmark
     * @param index The character offset of the bookmark position
     * @return Return the page number in the current pages array to be used with goToPage
     */
    private int fromIndexToPage(int index){
        if (index > content.length()) { return 0; }
        for (int i=0; i<pageStart.size()-1; i++){
            if (index < pageStart.get(i+1))
                return i;
        }
        return pageStart.size()-1;
    }

    /**
     * Event called when the user presses on the main TextView. Its position will be
     * communicated to the public interface method and managed from the parent activity.
     * @param position text poisition where the user pressed
     */
    public void onTextViewPositionSelected(int position) {
        createNewBookmarkDialogFragment(position);
    }


    /**
     * onDetach method overriding. Sets to null the event listener
     */
    @Override
    public void onDetach() {
        super.onDetach();
    }

    /**
     * Extract the sentence and send them to the TTS engine
     * @return Stop the execution if there is already some audio being prepared or if we reach the end
     */
    private void prepareTextToRead() {
        boolValues.put("waiting",true);

        ArrayMap<String, String> settings = settingManager.getSettings();
        ArrayMap<String, String> settingFATTS = new ArrayMap<>();
        settingFATTS.put("id",settings.get("id"));
        settingFATTS.put("language",settings.get("language"));
        settingFATTS.put("gender",settings.get("gender"));
        settingFATTS.put("speed",settings.get("speed"));

        int tempSentence = currentValues.get("currentSentence") + currentValues.get("nextSentenceLength");

        //End of content reached, reset values to beginning and restart
        if ( tempSentence >= content.length() ) {
            nextText = false;
            boolValues.put("fileEnd",true);
            return;
        }

        //Limit the text string that is being sent to the TTS engine to a maximum of400 characters
        String tempResult;
        if (tempSentence+400 < content.length()) {
            tempResult = content.substring(tempSentence, tempSentence + 400);
            tempResult = tempResult.substring(0, getWordStart(tempResult, tempResult.length()));
        } else {
            tempResult = content.substring(tempSentence, content.length());
        }

        int endPhrase;

        /* The pattern match sentences in the form of Any kind of punctuation until the first word,
        *  at least two word, until a . ! ? or a newline.
        *  If it's not found the whole string is sent to the TTS Engine being it max 400 characters long
         */
            Pattern pattern = Pattern.compile("(\\W*)(\\w+\\W?[^\\.!\\?\\r\\n]*(\\.\\w)?)*\\W*");
            Matcher matcher = pattern.matcher(tempResult);
        if(matcher.find()) {
            endPhrase = matcher.end();
        } else {
            endPhrase = tempResult.length();
        }

        String result = tempResult.substring(0,endPhrase);

        currentValues.put("nextSentenceLength", result.length());
        //This pattern check that the sentence does have a final . or ! or ? if it doesn't it's added
        Pattern patternEnd = Pattern.compile("((\\w*)[\\.!\\?]+\\W*)$");
        Matcher matcherEnd = patternEnd.matcher(result);
        if(!matcherEnd.find()) {
            result = result + ".";
        }

        if (result.length() > 1) {
            result = result.toLowerCase();
            Log.d("prepareTextToRead",result);
            synth.getTextToSpeech(result, settingFATTS);
        } else {
            currentValues.put("nextSentenceLength", 0);
        }
    }


    /**
     * Receive the audio and sync data from the TTS engine
     * The it checks if the data received is valid or there as an error on the request
     * @param result A Bundle containing a byte[] and a ArrayList<Integer>
     */
    @Override
    public void returnTTSData(Bundle result) {
        //Check if the activity is still alive when the async call come back
        if(getActivity() != null) {
            boolValues.put("waiting",false);
            if (result.getByteArray("audio") == null || result.getIntegerArrayList("tokens") == null) {
                if (audioPlayer != null && (audioPlayer.isPlaying() == true || boolValues.get("isPaused"))) {
                    boolValues.put("netErrorBoolean", true);
                    //netErrorBoolean = true;
                } else {
                    netError();
                }
                return;
            }
            bufferAudio.add(result.getByteArray("audio"));
            bufferSyncData.add(result.getIntegerArrayList("tokens"));
            startPlayer();
        }
    }

    /**
     * Start the player with the audio received, if some audio was already being played it's
     * added to the queue
     */
    public void startPlayer() {
        if (audioPlayer != null ) {
            if (boolValues.get("isPaused") || audioPlayer.isPlaying() == true) {
                boolValues.put("moreAudio", true);
            } else {
                startAudio();
            }
        }
    }

    /**
     * Receive the byte[] and prepare it for being played
     * This method use prepareAsync() on the player to avoid having the main process hang while the
     * audio is prepared
     * The onCompletionListener is used to start a new byte[] if the next sentence audio is ready
     */
    public void startAudio() {
        try {
            boolValues.put("waiting",true);
            currentValues.put("currentSentenceLength", currentValues.get("nextSentenceLength"));
            //currentSentenceLength =  nextSentenceLength;
            File tempAudio = File.createTempFile("Temp", "wav", getActivity().getCacheDir());
            FileOutputStream fos = new FileOutputStream(tempAudio);
            fos.write(bufferAudio.removeFirst());
            if (bufferAudio.isEmpty()) {
                boolValues.put("moreAudio", false);
                //moreAudio = false;
            }
            fos.close();
            audioPlayer.reset();
            FileInputStream fis = new FileInputStream(tempAudio);
            audioPlayer.setDataSource(fis.getFD());
            audioPlayer.prepareAsync();
            audioPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer player) {
                    boolValues.put("waiting",false);
                    if (!detached) {
                        currentValues.put("currentPosition", 0);
                        currentValues.put("nextPosition", 0);
                        nextText = false;
                        startSync();
                        //currentPosition = 0;
                        if (!(boolValues.get("isPaused"))) {
                            audioPlayer.start();
                            /**
                             * If the audio takes short time to prepare and it's playing consecutive
                             * files the time thread could be started twice, so it check if it's still
                             * running before starting another
                             */
                            if (getTimeThread == null || !getTimeThread.isAlive()) {
                                getTimeThread = new Thread(getCurrentTime);
                                getTimeThread.start();
                            }
                        }
                    }
                }
            });

            /**
             * If multiple audio is waiting this trigger the start for the next audio once the
             * first has finished
             */
            audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer player) {
                    currentContent.removeSpan(underline);
                    textView.setText(currentContent);
                    /**
                     * If a network error happened there is a stop here, this is not optimal because
                     * for the moment there is the possibility to have multiple audio file waiting
                     * when an error happens.
                     * It shouldn't happen on the final product when the audio request are made 1 at the time
                     */
                    currentValues.put("currentSentence", currentValues.get("currentSentence")+currentValues.get("currentSentenceLength"));
                    currentValues.put("currentSentenceLength", 0);
                    getTimeThread.interrupt();
                    //currentSentence = currentSentence + currentSentenceLength;
                    if (boolValues.get("netErrorBoolean")) { netError(); return; }
                    if (boolValues.get("moreAudio") == true) {
                        startAudio();
                    }
                    if (boolValues.get("fileEnd")) {
                        resetValues();
                    }
                }
            });
            //Remove the temporary file that is being reproduced to avoid memory issue
            tempAudio.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Runnable to keep up to date the highlight of the text
     * It has to run on a separate thread
     *
     * This runnable use a runOnUiThread to update the highlight, this will execute the called method
     * on the main thread as only it can update the view
     */
    private Runnable getCurrentTime = new Runnable() {
        public void run() {
            /**
             * The first sleep is needed  to create a little offset so the check is made correctly.
             * It's needed only if the sleep inside the while is set too 500+ ms
             *
             try {
             sleep(250);
             } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             return;
             }
             */
            int duration;
            while (audioPlayer.isPlaying()) {
                duration = audioPlayer.getDuration();
                final int currTime = audioPlayer.getCurrentPosition();
                if(!nextText && (duration - currTime) < 10000 ) {
                    nextText = true;
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            prepareTextToRead();
                        }
                    });
                }
                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        updateHighlight(currTime);
                    }
                });
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    };

    /**
     * When the audio start it takes the new data for the highlight of the text
     */
    public void startSync() {
        if (bufferSyncData != null) {
            currentSyncData = bufferSyncData.removeFirst();
        }
    }

    /**
     * Update the underline of the text
     * @param time the current time of the player
     */
    private void updateHighlight(int time) {
        //Avoid out of bound cases when the last word is read
        if (currentSyncData == null || currentSyncData.size() <= currentValues.get("nextPosition")) {
            return;
        }

        //Check if the word is in the same page, if not it takes care to change page
        if (checkTextNextPage(time)) { nextPage(); }

        //True if the current time is lower than the start time of the next word
        if(time >= (currentSyncData.get(currentValues.get("nextPosition")))) {
            currentValues.put("currentPosition", currentValues.get("nextPosition"));
            currentValues.put("nextPosition", currentValues.get("currentPosition") + 3);
            boolValues.put("updateHighlight", true);
        }

        //If it needs to update the Highlight it enters and set the right positions
        if (boolValues.put("updateHighlight", true)) {

            boolValues.put("updateHighlight", false);
            int temp = currentValues.get("currentSentence") - pageStart.get(currentValues.get("currentPage"));

            //Set the starting point of the current highlight considering the possibility of a word starting in the previous page
            int beginHighlight;
            if ( currentValues.get("currentSentence") + (currentSyncData.get(currentValues.get("currentPosition") + 1)) <= pageStart.get(currentValues.get("currentPage")) ) {
                beginHighlight = 0;
            } else if((pageStart.size() > currentValues.get("currentPage")+1) && currentValues.get("currentSentence") + (currentSyncData.get(currentValues.get("currentPosition") + 1)) >= pageStart.get(currentValues.get("currentPage")+1)) {
                beginHighlight = currentContent.length();
            } else {
                beginHighlight = temp + (currentSyncData.get(currentValues.get("currentPosition") + 1));
            }

            //Set the ending point of the current highlight considering the possibility of a word ending in the next page
            int endHighlight;
            if ( (pageStart.size() > currentValues.get("currentPage") + 1) && (currentValues.get("currentSentence") + currentSyncData.get(currentValues.get("currentPosition") + 2)) > pageStart.get(currentValues.get("currentPage")+1)  ) {
                endHighlight = currentContent.length();
                boolValues.put("updateHighlight", true);
            } else if(currentValues.get("currentSentence") + (currentSyncData.get(currentValues.get("currentPosition") + 2)) <= pageStart.get(currentValues.get("currentPage")))  {
                endHighlight = 0;
            } else {
                endHighlight = temp + (currentSyncData.get(currentValues.get("currentPosition") + 2));
            }

            /**
             * Set the highlight with the current values
             */
                currentContent.setSpan(underline, beginHighlight, endHighlight, 0);
            Log.d("Begin",String.valueOf(beginHighlight));
            Log.d("End",String.valueOf(endHighlight));

            textView.setText(currentContent);

        }
    }

    /**
     * Check if the next word of the sentence is in the next page
     * @return True is the next word is on the next page
     */
    private Boolean checkTextNextPage(int time) {
        if (pageStart.size() <= currentValues.get("currentPage") + 1) {
            return false;
        }
        if (currentValues.get("currentSentence") + currentSyncData.get(currentValues.get("nextPosition") + 1) >= pageStart.get(currentValues.get("currentPage") + 1)) {
            if (currentSyncData.get(currentValues.get("nextPosition")) - time < 80) {
                boolValues.put("pageEnded", true);
                currentValues.put("currentPosition", currentValues.get("nextPosition"));
                return true;
            }
        }
        if(currentValues.get("currentSentence") + currentSyncData.get(currentValues.get("currentPosition") + 2) >= pageStart.get(currentValues.get("currentPage") + 1))  {
            int wordLength = currentSyncData.get(currentValues.get("currentPosition") + 2) - currentSyncData.get(currentValues.get("currentPosition") + 1);
            int wordCut = pageStart.get(currentValues.get("currentPage") + 1) - currentSyncData.get(currentValues.get("currentPosition") + 1) + currentValues.get("currentSentence");
            int pageSkipTime = currentSyncData.get(currentValues.get("currentPosition")) + ((currentSyncData.get(currentValues.get("nextPosition")) - currentSyncData.get(currentValues.get("currentPosition"))) * wordCut / wordLength);
            if (pageSkipTime <= time) {
                boolValues.put("pageEnded", true);
                return true;
            }
        }
        return false;
    }

    /**
     * Start or Pause the media player
     * If it was paused the thread for the current time will end, so it will restart the thread
     */
    public void playPause() {
        //Case 1 is if i'm starting the player on the first time and it still doesn't exist
        if (audioPlayer == null && !nextText) {
            nextText = true;
            audioPlayer = new MediaPlayer();
            checkCorrectPage();
            prepareTextToRead();
            return;
        }

        //Case 2 is if i'm starting the player while it was idle and not playing or pause
        if(!(audioPlayer.isPlaying() || boolValues.get("isPaused"))  && !nextText) {
            nextText = true;
            checkCorrectPage();
            prepareTextToRead();
            return;
        }

        if(audioPlayer.isPlaying()) {
            //Case 3 is if the player is currently playing some audio
            pause();
        } else {
            //Case 4 is if the player has some audio but is paused
            play();
        }

    }

    /**
     * Highlight is based on the current displayed page
     * Here check if we are in the correct page and move us to it if not
     *
     * currentSentence is the start of the sentence that is being read
     * currentSyncData.get(currentPosition+1) is the start of the current word that is being read
     * pageStart.get(currentPage) is the start of the page that is being read
     * pageStart.get(currentPage+1) is the start of the next page
     *
     */
    public void checkCorrectPage() {
        int currentTextIndex = currentValues.get("currentSentence");
        //On the first check we still don't have any sync data
        if (currentSyncData != null && !currentSyncData.isEmpty()) { currentTextIndex = currentTextIndex + currentSyncData.get(currentValues.get("currentPosition")+1); }
        int getPage = fromIndexToPage(currentTextIndex);
        if (currentValues.get("currentPage") != getPage) {
            goToPage(getPage);
        }
    }

    /**
     * Check if the player was paused and in case restart it
     */
    public void play() {
        if (audioPlayer != null && boolValues.get("isPaused") == true ) {
            checkCorrectPage();
            boolValues.put("isPaused", false);
            //isPaused =  false;
            audioPlayer.start();
            //In case the thread for the timings was stopped it get started again
            if (getTimeThread== null || !getTimeThread.isAlive()) {
                getTimeThread = new Thread(getCurrentTime);
                getTimeThread.start();
            }
        }
    }

    /**
     * Check if the player isn't already paused and in case it pause it
     */
    public void pause() {
        if (audioPlayer != null && boolValues.get("isPaused") == false ) {
            boolValues.put("isPaused", true);
            //isPaused = true;
            audioPlayer.pause();
        }
    }

    /**
     * Should save the current position on a file so it can be reloaded when opened like a bookmark
     */
    public void saveCurrentPosition() {
        int docPosition = documentManager.getDocumentPosition(currentDocument.getTitle(), currentDocument.getImportDate());
        if (docPosition != -1) {
            currentDocument.setLastReadPosition(currentValues.get("currentSentence"));
            documentManager.getArrayListDocuments(getContext()).get(docPosition).setLastReadPosition(currentValues.get("currentSentence"));
            try {
                documentManager.saveDocuments(getContext());
            } catch (IOException e) {
            }
        }
    }

    /**
     * Show a simple alertDialog warning a network error happened
     */
    public void netError() {
        nextText = false;
        currentValues.put("nextSentenceLength",0);

        boolValues.put("netErrorBoolean", false);


        //Alternative dialog with a button, easier to see than a Toast

            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
            alertDialogBuilder.setMessage("C'è stato un errore con la connessione internet. Riprova.");
            alertDialogBuilder.setNegativeButton("Chiudi", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                }
            });

            AlertDialog alertDialog = alertDialogBuilder.create();
            alertDialog.show();

            //Toast, doesn't need to be clicked but it's harder to read
            //Toast.makeText(getContext(), "C'è stato un errore con la connessione internet. Riprova.", Toast.LENGTH_SHORT).show();

    }

    /**
     * Method that call the NewBookmarkDialogFragment for the creation of a new bookmark
     * @param position The position of the word that has been pressed
     */
    private void createNewBookmarkDialogFragment(int position){
        FragmentManager fragmentManager = getFragmentManager();
        NewBookmarkDialogFragment newBookmarkDialogFragment =
                NewBookmarkDialogFragment.newInstance(position, currentDocument);

        newBookmarkDialogFragment.show(fragmentManager,"bookmark_dialog");
    }

    /**
     * Start reading from a selected position
     * @param position The position in the textView that has been pressed
     */
    private void startReadingFrom(int position){
            if (audioPlayer != null) {
                audioPlayer.reset();
            }
            resetValues();
            int wordStart = getWordStart(currentContent.toString(), position);
            wordStart = wordStart + pageStart.get(currentValues.get("currentPage"));
            currentValues.put("currentSentence", wordStart);
            playPause();
    }

    /**
     *
     * @param text The string that contain the word for which i need to find the beginning
     * @param position The position of the text selected
     * @return The position of the first letter of the word that was clicked
     */
    private int getWordStart(String text, int position){
        int wordStart;
        wordStart = position;

        String temp = text.substring(0,position);

        //((?=\s?)[^\s]*$) Simply look for the last space or the beginning of the text if there are no spaces
        Pattern pattern = Pattern.compile("((?=\\s?)[^\\s]*$)");
        Matcher matcher = pattern.matcher(temp);
        if(matcher.find()) {
            wordStart = matcher.start();
        }

        return wordStart;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    /**
     * Function used to set all the important values at their initial status
     * Used both at startup or when it's needed to start reading from a different point
     */
    private void resetValues() {
        //currentValues.put("currentPage", 0);
        currentValues.put("currentSentence", 0);
        currentValues.put("nextSentenceLength", 0);
        if (bufferAudio != null ) bufferAudio.clear();
        if (bufferSyncData!= null ) bufferSyncData.clear();
        if (currentSyncData != null ) currentSyncData = null;

        boolValues.put("fileEnd",false);
        boolValues.put("updateHighlight", false);
        boolValues.put("moreAudio", false);
        boolValues.put("netErrorBoolean", false);
        boolValues.put("isPaused", false);
        boolValues.put("pageEnded", false);
        nextText = false;
        detached = false;
    }


    /**
     * Update the page number on the toolbar with the current page on screen
     */
    private void setToolbarPage() {
        ((Toolbar) getActivity().findViewById(R.id.toolbarSD)).getMenu().getItem(0).setTitle((currentValues.get("currentPage")+1) + " / " + (pages.size()) );
    }

    /**
     * Create a temporary dialog allowing to go to a selected page
     * If the page chosen is outside the pages array bound it does nothing
     */
    public void changePage() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.go_to_page);
        final EditText input = new EditText(getContext());
        input.setInputType(InputType.TYPE_CLASS_NUMBER);
        int currentPage = currentValues.get("currentPage") + 1;
        input.setText(String.valueOf(currentPage));
        builder.setView(input);
        builder.setPositiveButton("Ok",new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                int page = Integer.valueOf(input.getText().toString()) -1;
                if (page >= 0 && page < pages.size()) {
                    pause();
                    goToPage(page);
                } else {
                    Toast.makeText(getContext(), "Errore la pagina non è presente.", Toast.LENGTH_SHORT).show();
                }
            }
        });
        builder.setNegativeButton("Cancel",new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        AlertDialog alertDialog = builder.create();
        alertDialog.show();

    }

}