// !! unify showMessageBox and showWarningBox

begintemplate MessageWarningHelper

    public showMessageBox, showWarningBox, hideWarningBox, startIntercepting, endIntercepting
    
    external codeContractViolation
    external eachItemInList
    external enumArgTypes
    
    objref msgWarnBox
    
    intLevel = -1
    numInts = -1
    objref intMsgLines, intWarnLines
    strdef lastIntMsgTitle
    
    x = -1
    y = -1
    
    strdef warnTitle
    
    
    proc init() {
        intLevel = 0
        numInts = -1
        intMsgLines = new List()
        intWarnLines = new List()
        
        x = 500
        y = 300
        
        warnTitle = "Warning"
    }
    
    // Show the message box with "OK" button OR intercept all message lines and the title to show them later
    // Restriction: Cannot be called in the process of construction of a panel (applied if intLevel == 0)
    // $si, i from 1 to numarg()-1 - All lines of the message
    // $oi - (List of String-s) The lines of the message to be shown as a bulleted list
    // $si, i=numarg() - The widget title
    proc showMessageBox() { local numArg, i, argType
        
        numArg = numarg()
        
        if (intLevel > 0) {
            if (intMsgLines.count() != 0) {
                intMsgLines.append(new String("--------------------"))
            }
            // !! keep in sync with similar cycle below
            for i = 1, numArg - 1 {
                argType = argtype(i)
                if (argType == enumArgTypes.strdef_) {
                    intMsgLines.append(new String($si))
                } else if (argType == enumArgTypes.objref_) {
                    appendBulletedList(intMsgLines, $oi)
                } else {
                    codeContractViolation()
                }
            }
            lastIntMsgTitle = $si       // here i == numArg
            numInts += 1
            return
        }
        
        dismissHandler()
        
        msgWarnBox = new VBox()
        msgWarnBox.intercept(1)
        {
            xpanel("")
            // !! keep in sync with similar cycle above
            for i = 1, numArg - 1 {
                argType = argtype(i)
                if (argType == enumArgTypes.strdef_) {
                    xlabel($si)
                } else if (argType == enumArgTypes.objref_) {
                    showBulletedList($oi)
                } else {
                    codeContractViolation()
                }
            }
            xbutton("OK", "dismissHandler()")
            xpanel()
        }
        msgWarnBox.dismiss_action("dismissHandler()")
        msgWarnBox.intercept(0)
        msgWarnBox.map($si, x, y, -1, -1)   // here i == numArg
    }
    
    // Show warning box with "OK" button OR intercept all warning lines to show them later
    // Restriction: Cannot be called in the process of construction of a panel (applied if intLevel == 0)
    // $si - The lines of the warning
    // $oi - (List of String-s) The lines of the warning to be shown as a bulleted list
    proc showWarningBox() { local numArg, i, argType
        
        numArg = numarg()
        
        if (intLevel > 0) {
            if (intWarnLines.count() != 0) {
                intWarnLines.append(new String("--------------------"))
            }
            // !! keep in sync with similar cycle below
            for i = 1, numArg {
                argType = argtype(i)
                if (argType == enumArgTypes.strdef_) {
                    intWarnLines.append(new String($si))
                } else if (argType == enumArgTypes.objref_) {
                    appendBulletedList(intWarnLines, $oi)
                } else {
                    codeContractViolation()
                }
            }
            numInts += 1
            return
        }
        
        dismissHandler()
        
        msgWarnBox = new VBox()
        msgWarnBox.intercept(1)
        {
            xpanel("")
            // !! keep in sync with similar cycle above
            for i = 1, numArg {
                argType = argtype(i)
                if (argType == enumArgTypes.strdef_) {
                    xlabel($si)
                } else if (argType == enumArgTypes.objref_) {
                    showBulletedList($oi)
                } else {
                    codeContractViolation()
                }
            }
            xbutton("OK", "dismissHandler()")
            xpanel()
        }
        msgWarnBox.dismiss_action("dismissHandler()")
        msgWarnBox.intercept(0)
        msgWarnBox.map(warnTitle, x, y, -1, -1)
    }
    
    proc hideWarningBox() { localobj nil
        if (msgWarnBox != nil) {
            msgWarnBox.unmap()
            msgWarnBox = nil
        }
    }
    
    // Start intercepting all messages and warnings to show them later in a single box
    // It's safe to use the nested interception blocks
    proc startIntercepting() {
        intLevel += 1
        if (intLevel == 1) {
            numInts = 0
        }
    }
    
    // Show all intercepted messages and warnings in a single box
    // Restriction: Cannot be called in the process of construction of a panel
    proc endIntercepting() { local isSomeWarns, isSomeMsgs, isOnlyOneWarn, isOnlyOneMsg
        strdef title
        
        if (intLevel == 0) {
            codeContractViolation()
        }
        
        intLevel -= 1
        if (intLevel > 0) {
            return
        }
        
        if (numInts == 0) {
            return
        }
        
        dismissHandler()
        
        isSomeWarns = (intWarnLines.count() != 0)
        isSomeMsgs = (intMsgLines.count() != 0)
        isOnlyOneWarn = (numInts == 1 && !isSomeMsgs)
        isOnlyOneMsg = (numInts == 1 && !isSomeWarns)
        
        msgWarnBox = new VBox()
        msgWarnBox.intercept(1)
        {
            xpanel("")
            if (isSomeWarns) {
                dumpAllIntLines(intWarnLines, isOnlyOneWarn, warnTitle)
            }
            if (isSomeMsgs) {
                dumpAllIntLines(intMsgLines, isOnlyOneMsg, "Message")
            }
            xbutton("OK", "dismissHandler()")
            xpanel()
        }
        msgWarnBox.dismiss_action("dismissHandler()")
        msgWarnBox.intercept(0)
        if (isOnlyOneWarn) {
            title = warnTitle
        } else if (isOnlyOneMsg) {
            title = lastIntMsgTitle
        } else {
            title = "Warnings and messages"
        }
        msgWarnBox.map(title, x, y, -1, -1)
        
        numInts = -1
        intWarnLines.remove_all()
        intMsgLines.remove_all()
        lastIntMsgTitle = ""
    }
    
    // All next staff is private
    
    
    proc appendBulletedList() { localobj intLines, listOfStrs, strObj
        strdef str
        
        intLines = $o1
        listOfStrs = $o2
        
        for eachItemInList(strObj, listOfStrs) {
            sprint(str, "    * %s", strObj.s)
            intLines.append(new String(str))
        }
    }
    
    proc showBulletedList() { localobj listOfStrs, strObj
        strdef str
        
        listOfStrs = $o1
        
        for eachItemInList(strObj, listOfStrs) {
            sprint(str, "    * %s", strObj.s)
            xlabel(str)
        }
    }
    
    proc dumpAllIntLines() { local isOnlyOneWarnOrMsg localobj intLines, line
        strdef name, text
        
        intLines = $o1
        isOnlyOneWarnOrMsg = $2
        name = $s3
        
        if (!isOnlyOneWarnOrMsg) {
            sprint(text, "========== %s(s) ==========", name)
            xlabel(text)
        }
        for eachItemInList(line, intLines) {
            xlabel(line.s)
        }
        if (!isOnlyOneWarnOrMsg) {
            xlabel("")
        }
    }
    
    proc dismissHandler() { localobj nil
        if (msgWarnBox != nil) {
            msgWarnBox.unmap()
        }
    }
    
endtemplate MessageWarningHelper


objref mwh
mwh = new MessageWarningHelper()