package de.sebse.fuplanner.services.kvv.types;

import android.content.Context;

import com.google.android.gms.common.internal.Objects;

import org.jetbrains.annotations.NotNull;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * Created by sebastian on 29.10.17.
 */

public class Modules implements Iterable<Modules.Module>, Serializable {
    private SortedListModule list;
    private final String mUsername;
    private transient long mLastTimestamp = 0;
    //private transient Logger log = new Logger(this);
    private static final String FILE_NAME = "ModuleListSaving";
    private static final String FILE_NAME_TIMESTAMP = "ModuleListSavingTimestamp";

    public Modules(String username) {
        this.mUsername = username;
        this.list = new SortedListModule();
    }

    public void addModule(@Nullable Semester semester, HashSet<String> lvNumber, String title, LinkedHashSet<Lecturer> lecturer, String type, String description, String ID) {
        Module m = new Module(semester, lvNumber, title, lecturer, type, description, ID);
        this.list.add(m);
    }

    @NonNull
    @Override
    public String toString() {
        return this.list.toString();
    }

    @NonNull
    @Override
    public Iterator<Module> iterator() {
        return this.list.iterator();
    }

    public Iterator<Module> latestSemesterIterator() {
        return this.list.filteredIterator(this.list.getLatestSemester());
    }

    public int size() {
        return this.list.size();
    }

    public Module get(String id) {
        return this.list.getById(id);
    }

    public Module getByIndex(int index) {
        return this.list.get(index);
    }

    public static Modules load(Context context) throws IOException, ClassNotFoundException {
        FileInputStream fis = context.openFileInput(FILE_NAME);
        ObjectInputStream is = new ObjectInputStream(fis);
        Object readObject = is.readObject();
        if (!(readObject instanceof Modules))
            return null;
        Modules modules = (Modules) readObject;
        is.close();
        fis.close();

        fis = context.openFileInput(FILE_NAME_TIMESTAMP);
        is = new ObjectInputStream(fis);
        modules.mLastTimestamp = is.readLong();
        is.close();
        fis.close();

        return modules;
    }

    public boolean isNewerVersionInStorage(Context context) throws IOException {
        FileInputStream fis = context.openFileInput(FILE_NAME_TIMESTAMP);
        ObjectInputStream is = new ObjectInputStream(fis);
        boolean result = this.mLastTimestamp < is.readLong();
        is.close();
        fis.close();
        return result;
    }

    public void save(Context context) throws IOException {
        FileOutputStream fos = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(this);
        os.close();
        fos.close();

        fos = context.openFileOutput(FILE_NAME_TIMESTAMP, Context.MODE_PRIVATE);
        os = new ObjectOutputStream(fos);
        this.mLastTimestamp = System.currentTimeMillis();
        os.writeLong(this.mLastTimestamp);
        os.close();
        fos.close();
    }

    public void delete(Context context) {
        context.deleteFile(FILE_NAME);
    }

    public String getUsername() {
        return mUsername;
    }

    public boolean updateList(Modules modules) {
        SortedListModule old = this.list;
        this.list = modules.list;
        boolean isChanged = false;
        for (Module oldModule : old) {
            Module newModule = this.list.getById(oldModule.getID());
            if (newModule != null) {
                if (!isChanged && !hashEquals(oldModule, newModule))
                    isChanged = true;
                newModule.announcements = oldModule.announcements;
                newModule.assignments = oldModule.assignments;
                newModule.events = oldModule.events;
                newModule.gradebook = oldModule.gradebook;
                newModule.resources = oldModule.resources;
            } else {
                isChanged = true;
            }
        }
        if (this.list.size() != old.size())
            isChanged = true;
        return isChanged;
    }

    private boolean hashEquals(Module o1, Module o2) {
        if (o1 == null && o2 == null)
            return true;
        else if (o1 == null || o2 == null)
            return false;
        else
            return o1.hashCode() == o2.hashCode();
    }

    public class Module implements Serializable {
        @Nullable public final Semester semester;
        @NotNull final HashSet<String> lvNumber;
        @NotNull public final String title;
        @NotNull
        public final ArrayList<Lecturer> lecturer;
        @Nullable public final String type;
        @Nullable public final String description;
        @NotNull private final String ID;
        @Nullable public ArrayList<Announcement> announcements;
        @Nullable public AssignmentList assignments;
        @Nullable public EventList events;
        @Nullable public ArrayList<Grade> gradebook;
        @Nullable public ArrayList<Resource> resources;

        public float getGradebookPercent(){
            float maxPoint = 0;
            float userPoint = 0;
            if (gradebook != null) {
                for (Grade g : gradebook){
                    maxPoint += g.getMaxPoints();
                    userPoint += g.getPoints();
                }
            }
            if (maxPoint == 0)
                return 0;
            return userPoint/maxPoint;
        }

        private Module(@Nullable Semester semester, @NotNull HashSet<String> lvNumber, @NotNull String title, @NotNull LinkedHashSet<Lecturer> lecturer, @Nullable String type, @Nullable String description, @NotNull String ID) {

            title = title.replaceAll("(.*?) (S[0-9]{2}|W[0-9/]{5})", "$1");

            this.semester = semester;
            this.lvNumber = lvNumber;
            this.title = title;
            this.lecturer = new ArrayList<>(lecturer);
            this.type = type;
            this.description = description;
            this.ID = ID;
        }

        @NonNull
        public String getID() {
            return ID;
        }

        @NonNull
        @Override
        public String toString() {
            return "Semester: "+semester+
                    "\nlvNumber: "+lvNumber.toString()+
                    "\ntitle: "+title+
                    "\nlecturer: "+lecturer.toString()+
                    "\ntype: "+type+
                    "\nID: "+ID;
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(semester, lvNumber, title, lecturer, type, description, ID);
        }
    }
}
