package info.knacki.pass.git.entities;

import android.support.annotation.NonNull;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import info.knacki.pass.git.GitSha1;

public class GitCommit implements GitPackable {
    private final String fHash;
    private byte[] fTreeHash;
    private GitObject.GitTree fTree;
    private String fParent;
    private String fAuthor;
    private String fCommitter;
    private final String fMessage;
    private Date fTime;

    public GitCommit(String hash, String commitContent) {
        int pos = commitContent.indexOf('\0');

        if (pos >= 0)
            commitContent = commitContent.substring(pos +1);
        StringBuilder message = null;
        for (String line: commitContent.split("\n")) {
            if (message != null)
                message.append(line).append('\n');
            else if ("".equals(line))
                message = new StringBuilder();
            else if (line.startsWith("tree"))
                fTreeHash = GitSha1.StringToBytes(Util.RemoveHead(line));
            else if (line.startsWith("parent"))
                fParent = Util.RemoveHead(line);
            else if (line.startsWith("author"))
                fAuthor = Util.RemoveHead(line);
            else if (line.startsWith("committer"))
                fCommitter = Util.RemoveHead(line);
        }
        fHash = hash;
        fMessage = message == null ? "" : message.toString();
        fTree = null;
    }

    protected GitCommit(GitCommit parent, String author, String committer, String msg) {
        fAuthor = author;
        fCommitter = committer;
        fParent = parent != null ? parent.fHash : null;
        fMessage = msg;
        fHash = null;
        fTime = new Date();
    }

    public String GetMessage() {
        return fMessage;
    }

    public GitObject.GitTree GetTree() {
        return fTree;
    }

    public byte[] GetTreeHash() { return fTreeHash; }

    public GitObject.GitTree InitializeTree() {
        return fTree = new GitObject.GitTree(GetTreeHash());
    }

    public String GetAuthor() {
        return fAuthor;
    }

    public String GetCommitter() {
        return fCommitter;
    }

    public String GetParent() {
        return fParent;
    }

    public Date GetTime() {
        return fTime;
    }

    @Override
    public eType GetPackableType() {
        return eType.eType_Commit;
    }

    @Override
    public byte[] GetPack() {
        String dateStr = "";
        if (fTime != null)
            dateStr = " " +(fTime.getTime() / 1000) +" " +(new SimpleDateFormat("Z", Locale.US).format(fTime));
        String data = "tree " +(fTree == null ? GitSha1.BytesToString(fTreeHash) : GitSha1.BytesToString(fTree.GetHash())) +"\n" +
                (GetParent() != null ? ("parent " +GetParent() +"\n") : "")+
                "author " +GetAuthor() +dateStr +"\n" +
                "committer " +GetCommitter() +dateStr +"\n" +
                "\n" +
                fMessage +"\n";
        return data.getBytes();
    }

    public GitCommit SetTree(GitObject.GitTree tree) {
        fTree = tree;
        fTreeHash = GitSha1.getRawSha1OfPackable(tree);
        return this;
    }

    public String GetHash() { return fHash; }

    public static class Builder {
        private final GitCommit co;
        private final GitObject.GitTree fTree;
        private final Set<String> fToPack = new TreeSet<>();

        public Builder(GitCommit parent, String author, String authorEmail, String message) {
            String aut = author +" <" +authorEmail +">";
            co = new GitCommit(parent, aut, aut, message);
            fTree = new GitObject.GitTree(null, parent.GetTree());
        }

        public Builder(String author, String authorEmail, String message) {
            String aut = author +" <" +authorEmail +">";
            co = new GitCommit(null, aut, aut, message);
            fTree = GitObject.GitTree.CreateEmpty();
        }

        public GitCommit Build() {
            co.fTree = fTree;
            for (GitPackable go: PreparePack()) {
                if (go instanceof GitObject.GitTree)
                    ((GitObject.GitTree) go).GetHash();
            }
            return co;
        }

        public GitCommit.Builder AddFile(String relativeFilename, File f) {
            if (f.exists()) {
                GitObject obj = fTree.AddItem(relativeFilename, f);
                do {
                    fToPack.add(obj.GetGitPath());
                    obj = obj.GetParent();
                } while (obj != null);
            } else {
                GitObject obj = fTree.GetObjectFullPath(relativeFilename);
                if (obj != null)
                    do {
                        obj.GetParent().Remove(obj.fName);
                        fToPack.remove(obj.GetGitPath());
                        obj = obj.GetParent();
                    } while (obj != null && ((GitObject.GitTree) obj).fItems.size() == 0 && obj.GetParent() != null);
                for (String i: fToPack)
                    if (i.startsWith(relativeFilename +"/"))
                        fToPack.remove(i);
                while (obj != null) {
                    fToPack.add(obj.GetGitPath());
                    obj.fSha1 = null;
                    obj = obj.GetParent();
                }
            }
            return this;
        }

        public SortedSet<GitPackable> PreparePack() {
            TreeSet<GitPackable> packs = new TreeSet<>(new GitPackableUtil.Comparator());
            for (String i: fToPack)
                packs.add("".equals(i) ? fTree : fTree.GetObjectFullPath(i));
            packs.add(co);
            return packs;
        }
    }

    @Override @NonNull
    public String toString() {
        return "Commit " +fHash;
    }
}
