
proc APSKnobOpenHelp {} {
    return "Usage: APSKnobOpen \[-fileName <configFileName>\]\nStarts an instance of tclKnobs in server mode using given config file.\nReturns a unique <id> required by the other APSKnob procedures. Note you\nmust perform an APSKnobClose, lest ye leave a number of resources hanging."
}

set apsKnobID 0

proc APSKnobOpen {args} {
    #Loading the comm package in effect starts a communication server.
    #This is called the FirstServer.
    #When tclKnobs is executed it will also load the comm package.
    #This will start the SecondServer.
    #This code can interact with multiple "SecondServers" at the same time.
    #When starting tclKnobs we will tell it how to contact the FirstServer
    #tclKnob will then contact the FirstServer and tell us how we 
    #can contact the SecondServer.
    global apsKnobFirstServerID apsKnobSecondServerID apsKnobChildUp apsKnobID apsOpenKnobList
    set apsKnobChildUp 0
    set fileName ""
    APSStrictParseArguments {fileName}
    if {[info commands ::comm::comm] != "::comm:comm"} {
        package require comm
    }
    set apsKnobFirstServerID "[::comm::comm self]"
    
    # Start up an instance of tclKnobs, giving it our server ID
    if {[catch {exec tclKnobs -serverMode 1 -apsKnobFirstServerID $apsKnobFirstServerID &} pid]} {
        return -code error "Unable to start up tclKnobs in server mode: $pid"
    }
    # Wait for notification
    tkwait variable apsKnobChildUp
    set apsKnobSecondServerID($apsKnobID) $apsKnobChildUp
    
    if {[catch {::comm::comm send $apsKnobSecondServerID($apsKnobID) "openConfigFile $fileName"} result]} {
        catch {::comm::comm shutdown $apsKnobSecondServerID($apsKnobID)}
        return -code error "Unable to open knobFile: $result"
    } else {
        set apsOpenKnobList($apsKnobSecondServerID($apsKnobID)) $result
        set result $apsKnobSecondServerID($apsKnobID)
        incr apsKnobID
        return $result
    }
}

proc APSKnobGetKnobListHelp {} {
    return "Usage: APSKnobGetKnobList \[-id <id>\]\nReturns the list of knobs which were configured by APSKnobOpen."
}
proc APSKnobGetKnobList {args} {
    global apsOpenKnobList

    set id ""
    APSStrictParseArguments {id}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    } else {
        return $apsOpenKnobList($id)
    }
}

proc APSKnobChildUp {tclKnobsID} {
    global apsKnobChildUp
    set apsKnobChildUp $tclKnobsID
}

proc APSKnobCloseHelp {} {
    return "Usage: APSKnobClose \[-id <id>\]\nCloses knob session, terminating the tclKnob server and cleaning up resources."
}
proc APSKnobClose {args} {
    set id ""
    APSStrictParseArguments {id}
    global apsOpenKnobList
    if {[info exists apsOpenKnobList($id)]} {
        unset apsOpenKnobList($id)
        catch {::comm::comm shutdown $id}
    } else {
        return -code error "Unknown id"
    }
}

proc APSKnobTickHelp {} {
    return "Usage: APSKnobTick \[-id <id>\] \[-knob <knobName>\] \[-direction <dir>\]\nTick the given <knobName> \"up\" or \"down\"\nThe knob offset after the tick is returned."
}
proc APSKnobTick {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    set direction "up"

    APSStrictParseArguments {id knob direction}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob $knob.  Known knobs are:\n[join $apsOpenKnobList($id) \n]"
    }
    if {[string compare $direction "up"] && \
          [string compare $direction "down"]} {
        return -code error "direction must be up or down"
    }
    if {[catch {::comm::comm send $id "knobTick $knob $direction"} result]} {
        return -code error "failed: $result"
    } else {
        # Knob offset after tick is returned
        return $result
    }
}

proc APSKnobGainHelp {} {
    return "Usage: APSKnobGain \[-id <id>\] \[-knob <knobName>\] \[-direction <dir>\]\nTick the given <knobName> gain \"up\" or \"down\" "
}
proc APSKnobGain {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    set direction "up"

    APSStrictParseArguments {id knob direction}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob"
    }
    if {[string compare $direction "up"] && \
          [string compare $direction "down"]} {
        return -code error "direction must be up or down"
    }
    
    if {[catch {::comm::comm send $id "knobGain $knob $direction"} result]} {
        return -code error "failed: $result"
    } else {
        # Knob gain after change
        return $result
    }
}

proc APSKnobGetGainHelp {} {
    return "Usage: APSKnobGetGain \[-id <id>\] \[-knob <knobName>\]\nReturns the current gain setting for <knobName>."
}
proc APSKnobGetGain {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    APSStrictParseArguments {id knob}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob"
    }  

    if {[catch {::comm::comm send $id "getKnobGain $knob"} result]} {
        return -code error "failed: $result"
    } else {
        return $result
    }
}

proc APSKnobGetOffsetHelp {} {
    return "Usage: APSKnobGetOffset \[-id <id>\] \[-knob <knobName>\]\nReturns the current offset of <knobName>."
}
proc APSKnobGetOffset {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    APSStrictParseArguments {id knob}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob"
    }  

    if {[catch {::comm::comm send $id "getKnobOffset $knob"} result]} {
        return -code error "failed: $result"
    } else {
        return $result
    }
}

proc APSKnobRestoreGainHelp {} {
    return "Usage: APSKnobRestoreGain \[-id <id>\] \[-knob <knobName>\]\nRestore the gain of <knobName> to what it was at time of APSKnobOpen."
}
proc APSKnobRestoreGain {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    APSStrictParseArguments {id knob}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob"
    }  

    if {[catch {::comm::comm send $id "restoreGain $knob"} result]} {
        return -code error "failed: $result"
    } else {
        return $result
    }
}

proc APSKnobSaveHelp {} {
    return "Usage: APSKnobSave \[-id <id>\] \[-knob <knobName>\] \[-all 1\]\nSave complete state of <knobName>, or all knobs if -all option is set."
}
proc APSKnobSave {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    set all 0
    APSStrictParseArguments {id knob all}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob"
    }  
    if {[catch {::comm::comm send $id "knobSave $knob $all"} result]} {
        return -code error "failed: $result"
    } else {
        return $result
    }
}

proc APSKnobRestoreHelp {} {
    return "Usage: APSKnobRestore \[-id <id>\] \[-knob <knobName>\] \[-all 1\]\nSave complete state of <knobName>, or all knobs if -all option is set."
}
proc APSKnobRestore {args} {
    global apsOpenKnobList

    set id ""
    set knob ""
    set all 0
    APSStrictParseArguments {id knob all}

    if {![info exists apsOpenKnobList($id)]} {
        return -code error "Unknown id"
    }
    if {[lsearch -exact $apsOpenKnobList($id) $knob] == -1} {
        return -code error "Unknown knob"
    }  
    if {[catch {::comm::comm send $id "knobRestore $knob $all"} result]} {
        return -code error "failed: $result"
    } else {
        return $result
    }
}
