// Imports
import java.text.SimpleDateFormat

import com.gridnine.xtrip.common.l10n.model.LocaleHelper
import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.NestedEntityReference;
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.commission.ProductType
import com.gridnine.xtrip.common.model.dict.GdsName
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.entity.EntityStorageHelper;
import com.gridnine.xtrip.common.model.helpers.ProfileHelper
import com.gridnine.xtrip.common.model.profile.CashRegisterWorkplace
import com.gridnine.xtrip.common.model.profile.Pcc
import com.gridnine.xtrip.common.model.profile.PersonType
import com.gridnine.xtrip.common.model.profile.SalesPoint
import com.gridnine.xtrip.common.model.system.WorklogIndex
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.util.MiscUtil

// Styles
createStyle(name: 'title', h_alignment: 'CENTER', v_alignment: 'CENTER')
createStyle(name: 'titleH1', fontHeight: 14, parent: 'title')
createStyle(name: 'titleH2', fontHeight: 9, parent: 'title')
createStyle(name: 'titleH3', fontHeight: 7, parent: 'title')
createStyle(name: 'header', h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight: 7)
createStyle(name: 'columnHeader', wrapText: true, parent: 'header')
createStyle(name: 'rowHeader', wrapText: true, parent: 'header')
createStyle(name: 'data', h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight: 7)
createStyle(name: 'dataText', parent: 'data')
createStyle(name: 'dataDate', format: 'm/d/yy', parent: 'data')
createStyle(name: 'dataNumber', h_alignment: 'RIGHT', format: '#,##0.00', parent: 'data')
createStyle(name: 'dataInteger', h_alignment: 'RIGHT', format: '#,##0', parent: 'data')
createStyle(name: 'ahl', h_alignment: 'LEFT')
createStyle(name: 'ahc', h_alignment: 'CENTER')
createStyle(name: 'ahr', h_alignment: 'RIGHT')
createStyle(name: 'avt', v_alignment: 'TOP')
createStyle(name: 'avc', v_alignment: 'CENTER')
createStyle(name: 'avb', v_alignment: 'BOTTOM')
createStyle(name: 'aac', h_alignment: 'CENTER', v_alignment: 'CENTER')
createStyle(name: 'bold', fontBold: true)
createStyle(name: 'italic', fontItalic: true)
createStyle(name: 'bt', topBorder: 'THIN')
createStyle(name: 'bl', leftBorder: 'THIN')
createStyle(name: 'bb', bottomBorder: 'THIN')
createStyle(name: 'br', rightBorder: 'THIN')
createStyle(name: 'ba', topBorder: 'THIN', leftBorder: 'THIN', bottomBorder: 'THIN', rightBorder: 'THIN')
createStyle(name: 'grey25', foreground: 'GREY_25_PERCENT')
createStyle(name: 'locked', locked: true)

// Properties
def periodBeginParameter = parameters['key-report-params']?.periodBegin;
def periodEndParameter = parameters['key-report-params']?.periodEnd;
def agencyParameter = parameters['agency'];

// Comparator
def comparator = [ compare: {a, b ->
        MiscUtil.compare(a.toString(), b.toString())
    }] as Comparator

// Closures
def agency = {

    def organization = EntityStorage.get().resolve(agencyParameter)?.entity
    return organization ? ProfileHelper.getFullName(organization, LocaleHelper.getLocale('ru', 'RU'), false) : '?'
}

def period = {

    def format = new SimpleDateFormat('dd.MM.yyyy')
    return String.format('с %sг по %sг', periodBeginParameter != null ? format.format(periodBeginParameter) : '?', periodEndParameter != null ? format.format(periodEndParameter) : '?')
}

def logs = {

    SearchQuery query = new SearchQuery()

    query.getCriteria().getCriterions().add(SearchCriterion.ge(WorklogIndex.Property.type.name(), PersonType.AGENT))

    if(periodBeginParameter) {
        query.getCriteria().getCriterions().add(SearchCriterion.or(SearchCriterion.eq(WorklogIndex.Property.endDate.name(), null), SearchCriterion.ge(WorklogIndex.Property.endDate.name(), periodBeginParameter)))
    }

    if(periodEndParameter) {
        query.getCriteria().getCriterions().add(SearchCriterion.or(SearchCriterion.eq(WorklogIndex.Property.startDate.name(), null), SearchCriterion.le(WorklogIndex.Property.startDate.name(), periodEndParameter)))
    }

    List<WorklogIndex> worklogs = new ArrayList<WorklogIndex>()

    for(WorklogIndex worklog : EntityStorage.get().search(WorklogIndex.class, query).getData()) {

        EntityContainer<SalesPoint> cashRegisterWorkplaceContainer = EntityStorage.get().resolve(worklog.getCashRegisterWorkplace())

        if(cashRegisterWorkplaceContainer) {

            CashRegisterWorkplace cashRegisterWorkplace = cashRegisterWorkplaceContainer.getEntity()

            for(NestedEntityReference<SalesPoint, Pcc> pccReference : cashRegisterWorkplace.getPccs()) {

                Pcc pcc = EntityStorageHelper.resolve(pccReference, EntityStorageHelper.createContext());

                if(pcc.getGdsName() == GdsName.EXPRESS) {

                    worklogs.add(worklog)
                    break
                }
            }
        }
    }

    return worklogs
}

def days = { worklogs ->

    def daysSet = [] as Set
    def format = new SimpleDateFormat('yyyyMMdd')

    for(worklog in worklogs) {

        def startDate = MiscUtil.maximum(worklog.startDate, periodBeginParameter)
        def endDate = MiscUtil.minimum(worklog.endDate, periodEndParameter)
        
        if(!startDate || !endDate) {
            return Integer.MAX_VALUE
        }

        if(startDate.before(endDate)) {

            def startDateCalendar = Calendar.getInstance()
            def endDateCalendar = Calendar.getInstance()

            startDateCalendar.setTime(MiscUtil.clearTime(startDate))
            endDateCalendar.setTime(MiscUtil.clearTime(endDate))

            while (true) {

                daysSet.add(format.format(startDateCalendar.getTime()))
                startDateCalendar.add(Calendar.DAY_OF_MONTH, 1)

                if (startDateCalendar.compareTo(endDateCalendar) > 0) {
                    break;
                }
            }
        }
    }

    //        // For debug!
    //        text(daysSet.toString(), 'dataText|ba')
    //        nextRow()

    return daysSet.size()
}

page{'Реестр'}{

    // Set portrait mode
    landscape(false)

    // Set narrow margins
    margin(0.25, 0.25, 0.75, 0.75)

    // Set scale
    scale(100)

    // Set preserve mode
    preserve(false)

    def data = new TreeMap(comparator)

    def worklogsKey = 'worklogs'
    def tariffProductKey = 'tariff-product'
    def tariffMcoKey = 'tariff-mco'
    def penaltyKey = 'penalty'
    def blanksCountKey = 'blanks-count'
    def daysCountKey = 'days-count'

    def worklogs = logs();

    // Initialization
    for(worklog in worklogs) {

        def cashRegisterWorkplace = worklog.cashRegisterWorkplace

        if(!data[cashRegisterWorkplace]) {

            data[cashRegisterWorkplace] = [:]

            data[cashRegisterWorkplace][tariffProductKey] = 0
            data[cashRegisterWorkplace][tariffMcoKey] = 0
            data[cashRegisterWorkplace][penaltyKey] = 0
            data[cashRegisterWorkplace][blanksCountKey] = 0
            data[cashRegisterWorkplace][daysCountKey] = 0
        }

        if(!data[cashRegisterWorkplace][worklogsKey]) {
            data[cashRegisterWorkplace][worklogsKey] = []
        }

        data[cashRegisterWorkplace][worklogsKey] << worklog
    }

    for(entry in data) {

        tickets{

            for(worklog in entry.value[worklogsKey]) {

                if(MiscUtil.equals(it.salesPoint, worklog.salesPoint) && MiscUtil.equals(it.agent, worklog.employee) && MiscUtil.isBetween(worklog.startDate, worklog.endDate, it.issueDate)) {

                    if(it.productType == ProductType.RAILWAY_PRODUCT) {

                        entry.value[tariffProductKey] = MiscUtil.sum(entry.value[tariffProductKey], it.equivalentTotalFare)

                        if(it.status == ProductStatus.SELL) {
                            entry.value[blanksCountKey] += 1
                        }

                    } else if(it.productType == ProductType.RAILWAY_CRIMEA_MCO) {
                        entry.value[tariffMcoKey] = MiscUtil.sum(entry.value[tariffMcoKey], it.equivalentTotalFare)
                    }

                    entry.value[penaltyKey] = MiscUtil.sum(entry.value[penaltyKey], it.penalty)

                    break;
                }
            }
        }

        entry.value[daysCountKey] = days(entry.value[worklogsKey])
    }

    //    // For debug!
    //    text(String.valueOf(data), 'dataText|ahl')
    //    nextRow()

    // Report header
    text('Справка', 'titleH1|bold', 7, 1)
    nextRow()
    text("о работе железнодорожных касс  ${agency()}", 'titleH2|bold', 7, 1)
    nextRow()
    text("за период ${period()}", 'titleH2|bold', 7, 1)
    nextRow()

    columnWidth(12)
    nextColumn()
    columnWidth(17)
    nextColumn()
    columnWidth(17)
    nextColumn()
    columnWidth(17)
    nextColumn()
    columnWidth(17)
    nextColumn()
    columnWidth(12)
    nextColumn()
    columnWidth(12)
    nextRow()

    // Table header
    text('№кассы', 'columnHeader|ba')
    nextColumn()
    text('Тариф', 'columnHeader|ba')
    nextColumn()
    text('Ком.сбор', 'columnHeader|ba')
    nextColumn()
    text("КРС КРЫМ", 'columnHeader|ba')
    nextColumn()
    text("Кол.прод.док", 'columnHeader|ba')
    nextColumn()
    text("Кол.раб.дн", 'columnHeader|ba')
    nextColumn()
    text("Производит.", 'columnHeader|ba')
    nextRow()

    def count = 0

    // Table data
    for(entry in data) {

        text(entry.key.toString(), 'dataText|ba')
        nextColumn()
        number(entry.value[tariffProductKey], 'dataNumber|ba')
        nextColumn()
        number(entry.value[penaltyKey], 'dataNumber|ba')
        nextColumn()
        number(entry.value[tariffMcoKey], 'dataNumber|ba')
        nextColumn()
        number(entry.value[blanksCountKey], 'dataInteger|ahc|ba')
        nextColumn()
        number(entry.value[daysCountKey], 'dataInteger|ahc|ba')
        nextColumn()
        formula("${cellIndex(0, -2)}/${cellIndex(0, -1)}", 'dataInteger|ahc|ba')
        nextRow()

        count++
    }

    text('Итого', 'dataText|ba')
    nextColumn()

    3.times {

        formula(count > 0 ? "SUM(${cellIndex(-count, 0)}:${cellIndex(-1, 0)})" : null, 'dataNumber|ba')
        nextColumn()
    }

    formula(count > 0 ? "SUM(${cellIndex(-count, 0)}:${cellIndex(-1, 0)})" : null, 'dataInteger|ahc|ba')
    nextColumn()

    2.times {

        text(null, 'dataText|ba')
        nextColumn()
    }

    2.times {nextRow()}

    // Report footer
    nextColumn()
    text('Генеральный директор', 'titleH3|ahr')
    3.times {nextColumn()}
    text('________________________________', 'titleH3|ahl')
    2.times {nextRow()}
    nextColumn()
    text('Главный бухгалтер', 'titleH3|ahr')
    3.times {nextColumn()}
    text('________________________________', 'titleH3|ahl')
}
