package manager

import (
    "sync"
    "github/paxoscluster/recovery"
    "github/paxoscluster/proposal"
)

// Threadsafe proposal ID generator
type ProposalManager struct {
    roleId uint64
    proposalCount uint64
    currentId proposal.Id
    disk *recovery.Manager
    exclude sync.Mutex
}

// Constructor for the proposal manager
func ConstructProposalManager(roleId uint64, disk *recovery.Manager) (*ProposalManager, error) {
    id, err := disk.RecoverCurrentProposalId(roleId)
    if err != nil { return nil, err }

    newManager := ProposalManager {
        roleId: roleId,
        proposalCount: id.Sequence,
        currentId: id,
        disk: disk,
    }

    newManager.GenerateNextProposalId()
    return &newManager, nil
}

// Returns the most current proposal ID generated by this manager
func (this *ProposalManager) GetCurrentProposalId() proposal.Id {
    this.exclude.Lock()
    defer this.exclude.Unlock()
    
    return this.currentId
}

// Generates & returns a new proposal ID
func (this *ProposalManager) GenerateNextProposalId() (proposal.Id, error) {
    this.exclude.Lock()
    defer this.exclude.Unlock()

    this.proposalCount++
    this.currentId = proposal.ConstructProposalId(this.roleId, this.proposalCount)

    err := this.disk.UpdateCurrentProposalId(this.roleId, this.currentId)
    if err != nil { return proposal.Default(), err }

    return this.currentId, nil
}

// Generates & returns a new proposal ID which will outrank the given proposal ID
func (this *ProposalManager) GenerateProposalIdToBeat(hint proposal.Id) (proposal.Id, error) {
    this.exclude.Lock()
    defer this.exclude.Unlock()

    if this.proposalCount > hint.Sequence {
        this.proposalCount++
    } else {
        this.proposalCount = hint.Sequence + 1
    }

    this.currentId = proposal.ConstructProposalId(this.roleId, this.proposalCount)

    err := this.disk.UpdateCurrentProposalId(this.roleId, this.currentId)
    if err != nil { return proposal.Default(), err }

    return this.currentId, err
}
