package ca.etsmtl.applets.etsmobile.ui.fragment;

import android.Manifest;
import android.app.AlertDialog;
import android.content.ContentUris;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;
import android.widget.ViewSwitcher;

import com.google.android.material.snackbar.Snackbar;
import com.octo.android.robospice.persistence.exception.SpiceException;
import com.prolificinteractive.materialcalendarview.CalendarDay;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import com.prolificinteractive.materialcalendarview.OnDateSelectedListener;

import org.joda.time.DateTime;
import org.joda.time.DateTimeComparator;

import java.sql.SQLException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import butterknife.BindView;
import butterknife.ButterKnife;
import ca.etsmtl.applets.etsmobile.ApplicationManager;
import ca.etsmtl.applets.etsmobile.db.DatabaseHelper;
import ca.etsmtl.applets.etsmobile.model.Event;
import ca.etsmtl.applets.etsmobile.model.Seances;
import ca.etsmtl.applets.etsmobile.ui.adapter.SeanceAdapter;
import ca.etsmtl.applets.etsmobile.ui.calendar_decorator.CourseDecorator;
import ca.etsmtl.applets.etsmobile.ui.calendar_decorator.CourseTodayDecorator;
import ca.etsmtl.applets.etsmobile.ui.calendar_decorator.EventDecorator;
import ca.etsmtl.applets.etsmobile.ui.calendar_decorator.FinalExamDecorator;
import ca.etsmtl.applets.etsmobile.ui.calendar_decorator.TodayDecorator;
import ca.etsmtl.applets.etsmobile.util.HoraireManager;
import ca.etsmtl.applets.etsmobile.util.SignetsMethods;
import ca.etsmtl.applets.etsmobile.util.Utility;
import ca.etsmtl.applets.etsmobile.views.CustomProgressDialog;
import ca.etsmtl.applets.etsmobile2.R;
import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.OnNeverAskAgain;
import permissions.dispatcher.OnPermissionDenied;
import permissions.dispatcher.RuntimePermissions;

@RuntimePermissions
public class HoraireFragment extends HttpFragment implements Observer, OnDateSelectedListener {

    public static final String TAG = "HoraireFragment";
    private HoraireManager horaireManager;
    private CustomProgressDialog customProgressDialog;
    private DateTime dateTime = new DateTime();
    private SeanceAdapter seanceAdapter;//Seances d'une journee
    private SeanceAdapter allseanceAdapter;//Seances du semestre
    private SeanceAdapter upcomingseanceAdapter;//Seances du semestre
    private DatabaseHelper databaseHelper;
    private ProgressBar progressBarSyncHoraire;
    @BindView(R.id.calendarView)
    MaterialCalendarView mCalendarView;
    @BindView(R.id.horraireViewSwitcher)
    ViewSwitcher horraireViewSwitcher;
    @BindView(R.id.calendar_listview)
    ListView calendar_listview;
    private ArrayList<CalendarDay> courseDays;
    private ArrayList<CalendarDay> eventDays;
    private ArrayList<CalendarDay> finalExamDays;
    private  boolean listDisplay = true;

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_horaire, menu);
        super.onCreateOptionsMenu(menu, inflater);

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

            case R.id.menu_item_save_in_calendar:
                showCalendarPickerDialog();
                return true;
            case R.id.calendar_display_toggle:
                if(listDisplay){

                    item.setIcon(R.drawable.list_icon);
                    item.setTitle(R.string.list);
                    listDisplay = false;
                }else{
                    item.setIcon(R.drawable.icon_calendar);
                    item.setTitle(R.string.calendar);
                    listDisplay = true;
                }
                horraireViewSwitcher.showNext();
                return true;
        }

        return super.onOptionsItemSelected(item);

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.calendar_horaire_layout, container, false);
        ButterKnife.bind(this, v);


        databaseHelper = new DatabaseHelper(getActivity());

        seanceAdapter = new SeanceAdapter(getActivity());
        allseanceAdapter = new SeanceAdapter(getActivity());
        upcomingseanceAdapter = new SeanceAdapter(getActivity());

        fillSeancesList(dateTime.toDate());
        fillListView();
        setDaysList();

        calendar_listview.setAdapter(upcomingseanceAdapter);

        mCalendarView.setCurrentDate(new Date());
        mCalendarView.setSelectedDate(new Date());
        mCalendarView.setOnDateChangedListener(this);
        mCalendarView.addDecorators(
                new CourseDecorator(getActivity(),courseDays),
                new FinalExamDecorator(getActivity(),finalExamDays),
                new EventDecorator(eventDays,  ContextCompat.getColor(getActivity(),R.color.black)),
                new TodayDecorator(getActivity()),
                new CourseTodayDecorator(getActivity(),courseDays)
                );


        horaireManager = new HoraireManager(this, getActivity());
        horaireManager.addObserver(this);

        progressBarSyncHoraire = (ProgressBar) v.findViewById(R.id.progressBar_sync_horaire);
        progressBarSyncHoraire.setVisibility(ProgressBar.VISIBLE);

//        customProgressDialog = new CustomProgressDialog(getActivity(), R.drawable.loading_spinner, "Synchronisation en cours");
//        customProgressDialog.show();

        dataManager.getDataFromSignet(SignetsMethods.LIST_SESSION, ApplicationManager.userCredentials, this);
        dataManager.getDataFromSignet(SignetsMethods.LIST_SEANCES_CURRENT_AND_NEXT_SESSION, ApplicationManager.userCredentials, this);
        dataManager.getDataFromSignet(SignetsMethods.LIST_JOURSREMPLACES_CURRENT_AND_NEXT_SESSION, ApplicationManager.userCredentials, this);
        // @TODO Eventually, we might want to make the call for ETS Events here instead of in the onRequestSuccess.
        // The problem right now is getting the endDate without using the ListeDeSessions
        /*String dateStart = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        dataManager.sendRequest(new AppletsApiCalendarRequest(getActivity(), dateStart, "2016-04-30"), this);*/


        return v;

    }

    @Override
    public String getFragmentTitle() {
        return getString(R.string.menu_section_1_horaire);
    }

    @Override
    public void onRequestFailure(SpiceException arg0) {
        progressBarSyncHoraire.setVisibility(ProgressBar.GONE);
//        customProgressDialog.dismiss();
        if (getActivity() != null)
            Toast.makeText(getActivity(), getString(R.string.toast_Sync_Fail), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onRequestSuccess(final Object o) {
        horaireManager.onRequestSuccess(o);
    }

    @Override
    void updateUI() {

    }

    @Override
    public void update(Observable observable, Object data) {

//        customProgressDialog.dismiss();
        progressBarSyncHoraire.setVisibility(ProgressBar.GONE);

        fillSeancesList(dateTime.toDate());
    }

    public void fillListView(){

        try {
            List<Seances> seances = databaseHelper.getDao(Seances.class).queryForAll();
            List<Event> events = databaseHelper.getDao(Event.class).queryForAll();
            allseanceAdapter.setItemList(seances, events);


            List<Seances> upcomingSeances = new ArrayList<>();
            List<Event> upcomingEvents = new ArrayList<>();

            DateTime now = new DateTime();
            for(Seances sc : seances){
                DateTime scDate = DateTime.parse(sc.getDateDebut());
                if( DateTimeComparator.getDateOnlyInstance().compare(now, scDate) <= 0 ){
                    upcomingSeances.add(sc);
                }
            }
            for(Event ev : events){
                DateTime evDate = DateTime.parse(ev.getDateDebut());
                if( DateTimeComparator.getDateOnlyInstance().compare(now, evDate) <= 0 ){
                    upcomingEvents.add(ev);
                }
            }

            upcomingseanceAdapter.setItemList(upcomingSeances,upcomingEvents);

        } catch (SQLException e) {
            e.printStackTrace();
        }

        allseanceAdapter.notifyDataSetChanged();
        upcomingseanceAdapter.notifyDataSetChanged();
    }
    public void fillSeancesList(Date date) {
        if (getActivity() == null || !isAdded())
            return;

        SimpleDateFormat seancesFormatter = new SimpleDateFormat("yyyy-MM-dd", getResources().getConfiguration().locale);
        String today = seancesFormatter.format(date).toString();

        try {
            List<Seances> seances = databaseHelper.getDao(Seances.class)
                    .queryBuilder()
                    .where()
                    .like("dateDebut", today + "%")
                    .query();
            List<Event> events = databaseHelper.getDao(Event.class)
                    .queryBuilder()
                    .where()
                    .like("startDate", today + "%")
                    .query();
            seanceAdapter.setItemList(seances, events);

        } catch (SQLException e) {
            e.printStackTrace();
        }

        seanceAdapter.notifyDataSetChanged();
    }

    public void openCourseListDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        if (seanceAdapter.getCount() > 0) {
            builder.setAdapter(seanceAdapter, null);
            builder.setTitle(R.string.today_course);
        } else {
            builder.setTitle(R.string.empty_calendar);
        }
        builder.setNeutralButton(R.string.drawer_close, null);
        builder.create().show();
    }

    public void setDaysList() {

        courseDays = new ArrayList<>();
        finalExamDays = new ArrayList<>();
        eventDays = new ArrayList<>();

        try {
            ArrayList<Seances> seancesList = (ArrayList<Seances>) databaseHelper.getDao(Seances.class).queryForAll();
            for (Seances seance : seancesList) {

                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                Date seanceDay = formatter.parse(seance.dateDebut.substring(0, 10), new ParsePosition(0));
                if (seance.descriptionActivite.contains("final"))
                    finalExamDays.add(CalendarDay.from(seanceDay));
                else
                    courseDays.add(CalendarDay.from(seanceDay));
            }
            ArrayList<Event> eventList = (ArrayList<Event>) databaseHelper.getDao(Event.class).queryForAll();
            for (Event event : eventList) {
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                Date eventDay = formatter.parse(event.getDateDebut().substring(0, 10), new ParsePosition(0));
                eventDays.add(CalendarDay.from(eventDay));
            }



        } catch (SQLException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void onDateSelected(MaterialCalendarView widget, CalendarDay date, boolean selected) {
        widget.setSelectedDate(date);
        fillSeancesList(date.getDate());

        openCourseListDialog();
    }

    /**
     * Shows a dialog to the user inviting him/her to select a type of calendar. It is based between
     * three choices:
     *
     * Replaced days (Jours remplacés)
     * Courses (Séances)
     * ETS public calendar (Calendrier public ÉTS)
     */

    public void showCalendarPickerDialog() {

         /*
          Not sure if replaced days is even needed but let's leave it here since it has been
          given to us by the Signets API.
          */

        String[] eventsTypes = {getString(R.string.export_replaced_days_calendar), getString(R.string.export_courses_calendar), getString(R.string.export_public_events_calendar)};
        final boolean[] eventsSelection = {true, true, true};

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(R.string.export_calendar_dialog_title);
        builder.setMultiChoiceItems(eventsTypes, eventsSelection, new DialogInterface.OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i, boolean b) {
                eventsSelection[i] = b;
            }
        });

        builder.setPositiveButton(R.string.export_calendar_dialog_positive_button, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                HoraireFragmentPermissionsDispatcher.writeToCalendarWithPermissionCheck(HoraireFragment.this, eventsSelection[0], eventsSelection[1], eventsSelection[2]);
            }
        });

        builder.setNegativeButton(R.string.export_calendar_dialog_negative_button, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
            }
        });

        builder.show();
    }

    @Override
    public void onDetach() {
        super.onDetach();

        horaireManager.deleteObserver(this);
    }

    @NeedsPermission(Manifest.permission.WRITE_CALENDAR)
    void writeToCalendar(boolean tempJoursRemplacesEvent, boolean tempSeancesEvent, boolean tempCalPublicEvent) {
        new AsyncUpdateCalendar(tempJoursRemplacesEvent, tempSeancesEvent, tempCalPublicEvent).execute();
    }

    @OnPermissionDenied(Manifest.permission.WRITE_CALENDAR)
    @OnNeverAskAgain(Manifest.permission.WRITE_CALENDAR)
    void showPermissionsSnackBar() {
        Snackbar.make(getView(), R.string.export_calendar_allow_write_permission, Snackbar.LENGTH_SHORT)
                .setAction(R.string.action_settings, (listener) -> Utility.goToAppSettings(listener.getContext()))
                .setActionTextColor(ContextCompat.getColor(getActivity(), R.color.ets_red))
                .show();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        HoraireFragmentPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    }

    private class AsyncUpdateCalendar extends AsyncTask<Object, Void, Object> {

        /**
         *  Async class responsible for synchronizing the calendar in the background so the user
         *  experience isn't affected.
         */

        private boolean isJoursRemplacesSelected;
        private boolean isSeancesSelected;
        private boolean isCalPublicSelected;
        private Exception exception = null;

        public AsyncUpdateCalendar(boolean tempJoursRemplacesEvent, boolean tempSeancesEvent, boolean tempCalPublicEvent) {
            isJoursRemplacesSelected = tempJoursRemplacesEvent;
            isSeancesSelected = tempSeancesEvent;
            isCalPublicSelected = tempCalPublicEvent;
        }

        protected void onPreExecute() {
            customProgressDialog = new CustomProgressDialog(getActivity(), R.drawable.loading_spinner, getString(R.string.dialog_Updating_Calendar));
            customProgressDialog.show();
        }

        @Override
        protected Object doInBackground(Object... params) {
            try {
                horaireManager.updateCalendar(isJoursRemplacesSelected, isSeancesSelected, isCalPublicSelected);
            } catch (Exception e) {
                exception = e;
            }
            return null;
        }

        protected void onPostExecute(Object result) {

            customProgressDialog.dismiss();
            if (exception != null) {
                Toast.makeText(getActivity(), getString(R.string.toast_Calendar_Update_Error), Toast.LENGTH_SHORT).show();
            } else {

                //Launch native calendar app
                long startMillis = java.lang.System.currentTimeMillis();
                Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
                builder.appendPath("time");
                ContentUris.appendId(builder, startMillis);

                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(builder.build());

                startActivity(intent);

            }
        }
    }
    
}