module GitHUD.Git.Parse.Base (
  getGitRepoState
  ) where

import Control.Applicative ((<$>))
import Control.Concurrent (forkIO)
import Control.Concurrent.MVar (newEmptyMVar, takeMVar)

import GitHUD.Git.Types
import GitHUD.Git.Command
import GitHUD.Git.Parse.Status
import GitHUD.Git.Parse.Branch
import GitHUD.Git.Parse.Count

removeEndingNewline :: String -> String
removeEndingNewline str = concat . lines $ str

getGitRepoState :: IO GitRepoState
getGitRepoState = do
  -- Preparing MVars
  mvLocalBranch <- newEmptyMVar
  mvGitStatus <- newEmptyMVar
  mvRemoteName <- newEmptyMVar
  mvStashCount <- newEmptyMVar
  mvCommitShortSHA <- newEmptyMVar
  mvCommitTag <- newEmptyMVar

  forkIO $ gitCmdLocalBranchName mvLocalBranch
  forkIO $ gitCmdPorcelainStatus mvGitStatus
  forkIO $ gitCmdStashCount mvStashCount
  forkIO $ gitCmdCommitShortSHA mvCommitShortSHA
  forkIO $ gitCmdCommitTag mvCommitTag

  localBranchName <- removeEndingNewline <$> (takeMVar mvLocalBranch)
  forkIO $ gitCmdRemoteName localBranchName mvRemoteName

  remoteName <- removeEndingNewline <$> takeMVar mvRemoteName
  repoState <- gitParseStatus <$> takeMVar mvGitStatus
  stashCountStr <- takeMVar mvStashCount
  commitShortSHA <- removeEndingNewline <$> takeMVar mvCommitShortSHA
  commitTag <- removeEndingNewline <$> takeMVar mvCommitTag

  fillGitRemoteRepoState zeroGitRepoState {
    gitLocalRepoChanges = repoState
    , gitRemote = remoteName
    , gitLocalBranch = localBranchName
    , gitCommitShortSHA = commitShortSHA
    , gitCommitTag = commitTag
    , gitStashCount = (getCount stashCountStr)
  }

fillGitRemoteRepoState :: GitRepoState
                       -> IO GitRepoState
fillGitRemoteRepoState repoState@( GitRepoState { gitRemote = ""} ) = return repoState
fillGitRemoteRepoState repoState = do
  mvRemoteBranchName <- newEmptyMVar
  mvMergeBase <- newEmptyMVar
  mvCommitsToPull <- newEmptyMVar
  mvCommitsToPush <- newEmptyMVar

  forkIO $ gitCmdRemoteBranchName (gitLocalBranch repoState) mvRemoteBranchName
  forkIO $ gitCmdMergeBase (gitLocalBranch repoState) mvMergeBase
  remoteBranch <- removeEndingNewline <$> (takeMVar mvRemoteBranchName)
  mergeBase <- removeEndingNewline <$> (takeMVar mvMergeBase)

  let fullRemoteBranchName = buildFullyQualifiedRemoteBranchName (gitRemote repoState) remoteBranch

  -- TODO: gbataille - Check for merge-base. If none, don't execute remote part
  forkIO $ gitCmdRevToPush fullRemoteBranchName "HEAD" mvCommitsToPush
  forkIO $ gitCmdRevToPull fullRemoteBranchName "HEAD" mvCommitsToPull

  (mergeBranchToPull, mergeBranchToPush) <- getRemoteMasterMergeState mergeBase fullRemoteBranchName

  commitsToPushStr <- takeMVar mvCommitsToPush
  let commitsToPush = getCount commitsToPushStr
  commitsToPullStr <- takeMVar mvCommitsToPull
  let commitsToPull = getCount commitsToPullStr

  return repoState {
    gitRemoteTrackingBranch = remoteBranch
    , gitCommitsToPull = commitsToPull
    , gitCommitsToPush = commitsToPush
    , gitMergeBranchCommitsToPull = mergeBranchToPull
    , gitMergeBranchCommitsToPush = mergeBranchToPush
  }

getRemoteMasterMergeState :: String     -- ^ the merge base
                          -> String     -- ^ the fully qualified remote branch name
                          -> IO (Int, Int)    -- ^ tuple containing (to pull, to push)
getRemoteMasterMergeState "" _ = return (0, 0)
getRemoteMasterMergeState _ fullRemoteBranchName = do
  mvMergeBranchCommitsToPull <- newEmptyMVar
  mvMergeBranchCommitsToPush <- newEmptyMVar

  forkIO $ gitCmdRevToPush "origin/master" fullRemoteBranchName mvMergeBranchCommitsToPush
  forkIO $ gitCmdRevToPull "origin/master" fullRemoteBranchName mvMergeBranchCommitsToPull

  mergeBranchCommitsToRMasterStr <- takeMVar mvMergeBranchCommitsToPull
  let mergeBranchCommitsToRMaster = getCount mergeBranchCommitsToRMasterStr
  mergeBranchCommitsToMergeStr <- takeMVar mvMergeBranchCommitsToPush
  let mergeBranchCommitsToMerge = getCount mergeBranchCommitsToMergeStr

  return (mergeBranchCommitsToRMaster, mergeBranchCommitsToMerge)