package info.knacki.pass.git;

import java.util.logging.Logger;

import info.knacki.pass.git.entities.GitCommit;
import info.knacki.pass.git.entities.GitRef;
import info.knacki.pass.io.CharsetHelper;
import info.knacki.pass.io.OnResponseListener;
import info.knacki.pass.io.OnStreamResponseListener;
import info.knacki.pass.settings.SettingsManager;

public abstract class BaseGitProtocol implements GitInterface {
    protected final SettingsManager.Git fConfig;
    private final static Logger log = Logger.getLogger(BaseGitProtocol.class.getName());

    BaseGitProtocol(SettingsManager.Git config) {
        fConfig = config;
    }

    abstract protected void PushPack(GitCommit commit, Pacman pack, String branch, OnStreamResponseListener<Void> resp);
    abstract protected void FetchCommit(GitRef ref, OnStreamResponseListener<GitCommit> response);

    void GetHeadCommitFromRef(GitRef myRef, final OnStreamResponseListener<GitCommit> response) {
        if (myRef != null) {
            response.OnMsg("Checking out branch " +myRef.GetBranchName() +" revision " +myRef.GetHash());
            FetchCommit(myRef, new OnStreamResponseListener<GitCommit>() {
                @Override
                public void OnResponse(final GitCommit result) {
                    response.OnResponse(result);
                }

                @Override
                public void OnMsg(String message) {
                    response.OnMsg(message);
                }

                @Override
                public void OnError(String msg, Throwable e) {
                    response.OnError(msg, e);
                }
            });
        } else {
            response.OnError("Branch " +fConfig.GetBranch() + " not found on remote", null);
        }
    }

    protected GitRef GetRefFromBranchName(GitRef[] refs, String branchName) {
        if (refs != null)
            for (GitRef ref: refs)
                if (ref.GetBranch().equals(branchName))
                    return ref;
        return null;
    }

    GitRef GetHeadRef(GitRef[] refs) {
        return GetRefFromBranchName(refs, fConfig.GetBranch());
    }

    public void FetchHead(final OnStreamResponseListener<GitCommit> response) {
        GetRefs(new OnResponseListener<GitRef[]>() {
            @Override
            public void OnResponse(final GitRef[] result) {
                GitRef myRef = GetHeadRef(result);
                GetHeadCommitFromRef(myRef, response);
            }

            @Override
            public void OnError(final String msg, final Throwable e) {
                response.OnError(msg, e);
            }
        });
    }

    byte[] GitLine(String line) {
        String lineResult;
        if (line == null) {
            lineResult = "0000";
        } else {
            lineResult = String.format("%04x%s\n", line.length() +5, line);
        }
        return CharsetHelper.StringToByteArray(lineResult);
    }

    @Override
    public void PushCommitBuilder(GitCommit.Builder commit, OnStreamResponseListener<Void> resp) {
        PushPack(commit.Build(), new Pacman(commit.PreparePack()), fConfig.GetBranch(), resp);
    }

    protected abstract void OnBranchCreated();

    @Override
    public void CreateBranch(String branchName, OnStreamResponseListener<GitRef[]> callback) {
        final GitCommit.Builder commit = new GitCommit.Builder(fConfig.GetUsername(), fConfig.GetUserEmail(), "init");
        PushPack(commit.Build(), new Pacman(commit.PreparePack()), "refs/heads/" +branchName, new OnStreamResponseListener<Void>() {
            @Override
            public void OnMsg(String message) {
                callback.OnMsg(message);
            }

            @Override
            public void OnResponse(Void result) {
                OnBranchCreated();
                GetRefs(callback);
            }

            @Override
            public void OnError(String msg, Throwable e) {
                callback.OnError(msg, e);
            }
        });
    }
}
