import { ResponseError } from "../exception/ResponseError"
import { QueryErrorMessage } from "../types/ResponseTypes"
import { TeamSpeakQuery } from "./TeamSpeakQuery"
import { Version } from "../types/ResponseTypes"

export class Command {

  static SNAKE_CASE_IDENTIFIER = "_"
  private requestParser: Command.RequestParser = Command.getParsers().request
  private responseParser: Command.ResponseParser = Command.getParsers().response
  private cmd: string = ""
  private options: Command.options = {}
  private multiOpts: Command.multiOpts = []
  private flags: string[] = []
  private response: TeamSpeakQuery.Response = []
  private error: QueryErrorMessage|null = null
  private stack: string = new Error().stack!

  /** Initializes the Respone with default values */
  reset(): Command {
    this.response = []
    this.error = null
    return this
  }

  /** Sets the main command to send */
  setCommand(cmd: string): Command {
    this.cmd = cmd.trim()
    return this
  }

  /**
   * Sets the TeamSpeak Key Value Pairs
   * @param opts sets the Object with the key value pairs which should get sent to the TeamSpeak Query
   */
  setOptions(options: Command.options): Command {
    this.options = options
    return this
  }

  /**
   * retrieves the current set options for this command
   */
  getOptions(): Command.options {
    return this.options
  }

  /**
   * Sets the TeamSpeak Key Value Pairs
   * @param opts sets the Object with the key value pairs which should get sent to the TeamSpeak Query
   */
  setMultiOptions (options: Command.multiOpts): Command {
    this.multiOpts = options
    return this
  }

  /**
   * adds a customparser
   * @param parsers
   */
  setParser(parsers: Command.ParserCallback) {
    const { response, request } = parsers(Command.getParsers())
    this.requestParser = request
    this.responseParser = response
    return this
  }

  /** checks wether there are options used with this command */
  hasOptions(): boolean {
    return Object.values(this.options).length > 0 || this.hasMultiOptions()
  }

  /** checks wether there are options used with this command */
  hasMultiOptions() {
    return this.multiOpts.length > 0
  }

  /**
   * set TeamSpeak flags
   * @param flags sets the flags which should get sent to the teamspeak query
   */
  setFlags(flags: Command.flags): Command {
    this.flags = <string[]>flags
      .filter(flag => ["string", "number"].includes(typeof flag))
      .map(flag => String(flag))
    return this
  }

  /** checks wether there are flags used with this command */
  hasFlags(): boolean {
    return this.flags.length > 0
  }

  /**
   * set the Line which has been received from the TeamSpeak Query
   * @param line the line which has been received from the teamSpeak query
   */
  setResponse(line: string): Command {
    this.response = this.parse(line)
    return this
  }

  /**
   * Set the error line which has been received from the TeamSpeak Query
   * @param error the error line which has been received from the TeamSpeak Query
   */
  setError(raw: string): Command {
    this.error = <QueryErrorMessage><unknown>Command.parse({ raw })[0]
    return this
  }

  /** get the parsed error object which has been received from the TeamSpeak Query */
  getError() {
    if (!this.hasError()) return null
    return new ResponseError(this.error!, this.stack)
  }

  /** checks if a error has been received */
  hasError() {
    return (
      this.error !== null &&
      typeof this.error === "object" &&
      typeof this.error.id === "string" &&
      this.error.id !== "0"
    )
  }

  /** get the parsed response object which has been received from the TeamSpeak Query */
  getResponse() {
    return this.response
  }

  /** runs the parser of this instance */
  parse(raw: string) {
    return this.responseParser({ raw, cmd: Command })
  }

  /** runs the parser of this instance */
  build() {
    return this.requestParser(this)
  }

  /**
   * retrieves the default parsers
   */
  static getParsers(): Command.Parsers {
    return {
      response: Command.parse,
      request: Command.build
    }
  }

  /**
   * parses a snapshot create request
   * @param param0 the custom snapshot response parser
   */
  static parseSnapshotCreate({ raw }: Pick<Command.ParserArgument, "raw">) {
    const version = raw.match(/version=(\d+)/)
    if (!version) throw new Error("unable to detect snapshot version")
    switch (version[1]) {
      case "2":
        return (() => {
          const [data, snapshot] = raw.split("|")
          return <TeamSpeakQuery.Response>[{
            ...Command.parse({ raw: data })[0], snapshot
          }]
        })()
      case "3":
        return (() => {
          const { salt, data } = Command.parse({ raw })[0]
          return <TeamSpeakQuery.Response>[{
            version: "3", salt, snapshot: data
          }]
        })()
      default:
        throw new Error(`unsupported snapshot version: ${version[1]}`)
    }
  }

  /**
   * the custom snapshot request parser
   * @param data snapshot string
   * @param cmd command object
   */
  static buildSnapshotDeploy(data: string, cmd: Command, { version }: Version, snapshotVersion: string = "0") {
    if ((snapshotVersion === "0" && Command.minVersion("3.12.0", version)) || snapshotVersion === "3") {
      cmd.setOptions({ ...cmd.getOptions(), version: "3", data })
      return Command.build(cmd)
    } else if ((snapshotVersion === "0" && Command.minVersion("3.10.0", version)) || snapshotVersion === "2") {
      cmd.setOptions({ ...cmd.getOptions(), version: "2" })
      return [Command.build(cmd), data].join("|")
    } else {
      throw new Error(`unsupported teamspeak version (${version}) or snapshot version (${snapshotVersion})`)
    }
  }

  /**
   * checks if a version string has a minimum of x
   * @param minimum minimum the version string should have
   * @param version version string to compare
   */
  static minVersion(minimum: string, version: string) {
    const v = version.split(".").map(n => parseInt(n, 10))
    return minimum
      .split(".")
      .map(n => parseInt(n, 10))
      .every((n, index) => n <= v[index])
  }

  /**
   * parses a query response
   * @param data the query response received
   */
  static parse({ raw }: Pick<Command.ParserArgument, "raw">): TeamSpeakQuery.Response {
    return raw
      .split("|")
      .map(entry => {
        const res: TeamSpeakQuery.ResponseEntry = {}
        entry.split(" ").forEach(str => {
          const { key, value } = Command.getKeyValue(str)
          res[key] = Command.parseValue(key, value)
        })
        return res
      })
      .map((entry, _, original) => ({...original[0], ...entry }))
  }

  /**
   * Checks if a error has been received
   * @return The parsed String which is readable by the TeamSpeak Query
   */
  static build(command: Command) {
    let cmd = Command.escape(command.cmd)
    if (command.hasFlags()) cmd += ` ${command.buildFlags()}`
    if (command.hasOptions()) cmd += ` ${command.buildOptions()}`
    return cmd
  }

  /**
   * builds the query string for options
   * @return the parsed String which is readable by the TeamSpeak Querytt
   */
  buildOptions() {
    const options = this.buildOption(this.options)
    if (!this.hasMultiOptions()) return options
    return `${options} ${this.multiOpts.map(this.buildOption.bind(this)).join("|")}`
  }

  /** builds the query string for options */
  buildOption(options: Record<string, any>): string {
    return Object
      .keys(options)
      .filter(key => ![undefined, null].includes(options[key]))
      .filter(key => typeof options[key] !== "number" || !isNaN(options[key]))
      .map(key => Command.escapeKeyValue(key, options[key]))
      .join(" ")
  }

  /** builds the query string for flags */
  buildFlags(): string {
    return this.flags.map(f => Command.escape(f)).join(" ")
  }

  /**
   * escapes a key value pair
   * @param {string} key the key used
   * @param {string|string[]} value the value or an array of values
   * @return the parsed String which is readable by the TeamSpeak Query
   */
  static escapeKeyValue(key: string, value: string|string[]|boolean): string {
    key = Command.toSnakeCase(key)
    if (typeof value === "boolean") value = value ? "1" : "0"
    if (Array.isArray(value)) {
      return value.map(v => `${Command.escape(key)}=${Command.escape(v)}`).join("|")
    } else {
      return `${Command.escape(key)}=${Command.escape(value)}`
    }
  }

  /**
   * retrieves the key value pair from a string
   * @param str the key value pair to unescape eg foo=bar
   */
  static getKeyValue(str: string): { key: string, value: string|undefined } {
    const index = str.indexOf("=")
    if (index === -1) return { key: Command.toCamelCase(str), value: undefined }
    const value = str.substring(index+1)
    return {
      key: Command.toCamelCase(str.substring(0, index)),
      value: value === "" ? undefined : value
    }
  }

  /**
   * Parses a value to the type which the key represents
   * @param k the key which should get looked up
   * @param v the value which should get parsed
   */
  static parseValue(k: string, v: string|undefined) {
    if (v === undefined) return undefined
    if (Object.keys(Command.Identifier).includes(k)) {
      return Command.Identifier[k](v)
    } else {
      return this.parseString(v)
    }
  }

  /**
   * parses a number
   * @param value string to parse
   */
  static parseBoolean(value: string) {
    return Command.parseNumber(value) === 1
  }

  /**
   * parses a string value
   * @param value string to parse
   */
  static parseString(value: string) {
    return Command.unescape(value)
  }

  static parseRecursive(value: string) {
    return Command.parse({ raw: Command.unescape(value) })
  }

  /**
   * parses a string array
   * @param value string to parse
   */
  static parseStringArray(value: string) {
    return value.split(",").map(v => Command.parseString(v))
  }

  /**
   * parses a number
   * @param value string to parse
   */
  static parseNumber(value: string) {
    return parseFloat(value)
  }

  /**
   * parses a number array
   * @param value string to parse
   */
  static parseNumberArray(value: string) {
    return value.split(",").map(v => Command.parseNumber(v))
  }

  /** unescapes a string */
  static unescape(str: string): string {
    return String(str)
      .replace(/\\s/g, " ")
      .replace(/\\p/g, "|")
      .replace(/\\n/g, "\n")
      .replace(/\\f/g, "\f")
      .replace(/\\r/g, "\r")
      .replace(/\\t/g, "\t")
      .replace(/\\v/g, "\v")
      .replace(/\\\//g, "/")
      .replace(/\\\\/g, "\\")
  }

  /** escapes a string */
  static escape(str: string): string {
    return String(str)
      .replace(/\\/g, "\\\\")
      .replace(/\//g, "\\/")
      .replace(/\|/g, "\\p")
      .replace(/\n/g, "\\n")
      .replace(/\r/g, "\\r")
      .replace(/\t/g, "\\t")
      .replace(/\v/g, "\\v")
      .replace(/\f/g, "\\f")
      .replace(/ /g, "\\s")
  }

  /** converts a string to camel case */
  static toCamelCase(str: string) {
    let toUpper = false
    return str.split("").map(char => {
      if (char === Command.SNAKE_CASE_IDENTIFIER) {
        toUpper = true
        return ""
      } else if (toUpper) {
        toUpper = false
        return char.toUpperCase()
      } else {
        return char
      }
    }).join("")
  }

  /** converts a string to snake case */
  static toSnakeCase(str: string) {
    return str.split("").map(char => {
      const lower = char.toLowerCase()
      if (char !== lower) return `${Command.SNAKE_CASE_IDENTIFIER}${lower}`
      return char
    }).join("")
  }
}

export namespace Command {

  export interface ParserArgument {
    cmd: typeof Command
    raw: string
  }
  export interface Parsers {
    response: ResponseParser
    request: RequestParser
  }
  export type ParserCallback = (parser: Parsers) => Parsers
  export type ResponseParser = (data: ParserArgument) => TeamSpeakQuery.Response
  export type RequestParser = (cmd: Command) => string
  export type options = Record<string, TeamSpeakQuery.ValueTypes>
  export type multiOpts = Command.options[]
  export type flags = (number|string|null)[]

  export const Identifier = {
    sid: Command.parseString,
    serverId: Command.parseString,
    virtualserverNickname: Command.parseString,
    virtualserverUniqueIdentifier: Command.parseString,
    virtualserverName: Command.parseString,
    virtualserverWelcomemessage: Command.parseString,
    virtualserverPlatform: Command.parseString,
    virtualserverVersion: Command.parseString,
    virtualserverMaxclients: Command.parseNumber,
    virtualserverPassword: Command.parseString,
    virtualserverClientsonline: Command.parseNumber,
    virtualserverChannelsonline: Command.parseNumber,
    virtualserverCreated: Command.parseNumber,
    virtualserverUptime: Command.parseNumber,
    virtualserverCodecEncryptionMode: Command.parseNumber,
    virtualserverHostmessage: Command.parseString,
    virtualserverHostmessageMode: Command.parseNumber,
    virtualserverFilebase: Command.parseString,
    virtualserverDefaultServerGroup: Command.parseString,
    virtualserverDefaultChannelGroup: Command.parseString,
    virtualserverFlagPassword: Command.parseBoolean,
    virtualserverDefaultChannelAdminGroup: Command.parseString,
    virtualserverMaxDownloadTotalBandwidth: Command.parseNumber,
    virtualserverMaxUploadTotalBandwidth: Command.parseNumber,
    virtualserverHostbannerUrl: Command.parseString,
    virtualserverHostbannerGfxUrl: Command.parseString,
    virtualserverHostbannerGfxInterval: Command.parseNumber,
    virtualserverComplainAutobanCount: Command.parseNumber,
    virtualserverComplainAutobanTime: Command.parseNumber,
    virtualserverComplainRemoveTime: Command.parseNumber,
    virtualserverMinClientsInChannelBeforeForcedSilence: Command.parseNumber,
    virtualserverPrioritySpeakerDimmModificator: Command.parseNumber,
    virtualserverId: Command.parseString,
    virtualserverAntifloodPointsNeededPluginBlock: Command.parseNumber,
    virtualserverAntifloodPointsTickReduce: Command.parseNumber,
    virtualserverAntifloodPointsNeededCommandBlock: Command.parseNumber,
    virtualserverAntifloodPointsNeededIpBlock: Command.parseNumber,
    virtualserverClientConnections: Command.parseNumber,
    virtualserverQueryClientConnections: Command.parseNumber,
    virtualserverHostbuttonTooltip: Command.parseString,
    virtualserverHostbuttonUrl: Command.parseString,
    virtualserverHostbuttonGfxUrl: Command.parseString,
    virtualserverQueryclientsonline: Command.parseNumber,
    virtualserverDownloadQuota: Command.parseNumber,
    virtualserverUploadQuota: Command.parseNumber,
    virtualserverMonthBytesDownloaded: Command.parseNumber,
    virtualserverMonthBytesUploaded: Command.parseNumber,
    virtualserverTotalBytesDownloaded: Command.parseNumber,
    virtualserverTotalBytesUploaded: Command.parseNumber,
    virtualserverPort: Command.parseNumber,
    virtualserverAutostart: Command.parseNumber,
    virtualserverMachineId: Command.parseString,
    virtualserverNeededIdentitySecurityLevel: Command.parseNumber,
    virtualserverLogClient: Command.parseNumber,
    virtualserverLogQuery: Command.parseNumber,
    virtualserverLogChannel: Command.parseNumber,
    virtualserverLogPermissions: Command.parseNumber,
    virtualserverLogServer: Command.parseNumber,
    virtualserverLogFiletransfer: Command.parseNumber,
    virtualserverMinClientVersion: Command.parseNumber,
    virtualserverNamePhonetic: Command.parseString,
    virtualserverIconId: Command.parseString,
    virtualserverReservedSlots: Command.parseNumber,
    virtualserverTotalPacketlossSpeech: Command.parseNumber,
    virtualserverTotalPacketlossKeepalive: Command.parseNumber,
    virtualserverTotalPacketlossControl: Command.parseNumber,
    virtualserverTotalPacketlossTotal: Command.parseNumber,
    virtualserverTotalPing: Command.parseNumber,
    virtualserverIp: Command.parseStringArray,
    virtualserverWeblistEnabled: Command.parseNumber,
    virtualserverAskForPrivilegekey: Command.parseNumber,
    virtualserverHostbannerMode: Command.parseNumber,
    virtualserverChannelTempDeleteDelayDefault: Command.parseNumber,
    virtualserverMinAndroidVersion: Command.parseNumber,
    virtualserverMinIosVersion: Command.parseNumber,
    virtualserverStatus: Command.parseString,
    connectionFiletransferBandwidthSent: Command.parseNumber,
    connectionFiletransferBandwidthReceived: Command.parseNumber,
    connectionFiletransferBytesSentTotal: Command.parseNumber,
    connectionFiletransferBytesReceivedTotal: Command.parseNumber,
    connectionPacketsSentSpeech: Command.parseNumber,
    connectionBytesSentSpeech: Command.parseNumber,
    connectionPacketsReceivedSpeech: Command.parseNumber,
    connectionBytesReceivedSpeech: Command.parseNumber,
    connectionPacketsSentKeepalive: Command.parseNumber,
    connectionBytesSentKeepalive: Command.parseNumber,
    connectionPacketsReceivedKeepalive: Command.parseNumber,
    connectionBytesReceivedKeepalive: Command.parseNumber,
    connectionPacketsSentControl: Command.parseNumber,
    connectionBytesSentControl: Command.parseNumber,
    connectionPacketsReceivedControl: Command.parseNumber,
    connectionBytesReceivedControl: Command.parseNumber,
    connectionPacketsSentTotal: Command.parseNumber,
    connectionBytesSentTotal: Command.parseNumber,
    connectionPacketsReceivedTotal: Command.parseNumber,
    connectionBytesReceivedTotal: Command.parseNumber,
    connectionBandwidthSentLastSecondTotal: Command.parseNumber,
    connectionBandwidthSentLastMinuteTotal: Command.parseNumber,
    connectionBandwidthReceivedLastSecondTotal: Command.parseNumber,
    connectionBandwidthReceivedLastMinuteTotal: Command.parseNumber,
    connectionPacketlossTotal: Command.parseNumber,
    connectionPing: Command.parseNumber,
    clid: Command.parseString,
    clientId: Command.parseString,
    cldbid: Command.parseString,
    clientDatabaseId: Command.parseString,
    clientChannelId: Command.parseString,
    clientOriginServerId: Command.parseString,
    clientNickname: Command.parseString,
    clientType: Command.parseNumber,
    clientAway: Command.parseBoolean,
    clientAwayMessage: Command.parseString,
    clientFlagTalking: Command.parseBoolean,
    clientInputMuted: Command.parseBoolean,
    clientOutputMuted: Command.parseBoolean,
    clientInputHardware: Command.parseBoolean,
    clientOutputHardware: Command.parseBoolean,
    clientTalkPower: Command.parseNumber,
    clientIsTalker: Command.parseBoolean,
    clientIsPrioritySpeaker: Command.parseNumber,
    clientIsRecording: Command.parseBoolean,
    clientIsChannelCommander: Command.parseBoolean,
    clientUniqueIdentifier: Command.parseString,
    clientServergroups: Command.parseStringArray,
    clientChannelGroupId: Command.parseString,
    clientChannelGroupInheritedChannelId: Command.parseString,
    clientVersion: Command.parseString,
    clientPlatform: Command.parseString,
    clientIdleTime: Command.parseNumber,
    clientCreated: Command.parseNumber,
    clientLastconnected: Command.parseNumber,
    clientIconId: Command.parseString,
    clientCountry: Command.parseString,
    clientEstimatedLocation: Command.parseString,
    clientOutputonlyMuted: Command.parseNumber,
    clientDefaultChannel: Command.parseString,
    clientMetaData: Command.parseString,
    clientVersionSign: Command.parseString,
    clientSecurityHash: Command.parseString,
    clientLoginName: Command.parseString,
    clientLoginPassword: Command.parseString,
    clientTotalconnections: Command.parseNumber,
    clientFlagAvatar: Command.parseString,
    clientTalkRequest: Command.parseNumber,
    clientTalkRequestMsg: Command.parseString,
    clientMonthBytesUploaded: Command.parseNumber,
    clientMonthBytesDownloaded: Command.parseNumber,
    clientTotalBytesUploaded: Command.parseNumber,
    clientTotalBytesDownloaded: Command.parseNumber,
    clientNicknamePhonetic: Command.parseString,
    clientDefaultToken: Command.parseString,
    clientBadges: Command.parseString,
    clientBase64HashClientUID: Command.parseString,
    connectionConnectedTime: Command.parseNumber,
    connectionClientIp: Command.parseString,
    clientMyteamspeakId: Command.parseString,
    clientIntegrations: Command.parseString,
    clientDescription: Command.parseString,
    clientNeededServerqueryViewPower: Command.parseNumber,
    clientMyteamspeakAvatar: Command.parseString,
    clientSignedBadges: Command.parseString,
    clientLastip: Command.parseString,
    cid: Command.parseString,
    pid: Command.parseString,
    cpid: Command.parseString,
    order: Command.parseNumber,
    channelOrder: Command.parseNumber,
    channelName: Command.parseString,
    channelPassword: Command.parseString,
    channelDescription: Command.parseString,
    channelTopic: Command.parseString,
    channelFlagDefault: Command.parseBoolean,
    channelFlagPassword: Command.parseBoolean,
    channelFlagPermanent: Command.parseBoolean,
    channelFlagSemiPermanent: Command.parseBoolean,
    channelFlagTemporary: Command.parseBoolean,
    channelCodec: Command.parseNumber,
    channelCodecQuality: Command.parseNumber,
    channelNeededTalkPower: Command.parseNumber,
    channelIconId: Command.parseString,
    totalClientsFamily: Command.parseNumber,
    channelMaxclients: Command.parseNumber,
    channelMaxfamilyclients: Command.parseNumber,
    totalClients: Command.parseNumber,
    channelNeededSubscribePower: Command.parseNumber,
    channelCodecLatencyFactor: Command.parseNumber,
    channelCodecIsUnencrypted: Command.parseNumber,
    channelSecuritySalt: Command.parseString,
    channelDeleteDelay: Command.parseNumber,
    channelFlagMaxclientsUnlimited: Command.parseBoolean,
    channelFlagMaxfamilyclientsUnlimited: Command.parseBoolean,
    channelFlagMaxfamilyclientsInherited: Command.parseBoolean,
    channelFilepath: Command.parseString,
    channelForcedSilence: Command.parseNumber,
    channelNamePhonetic: Command.parseString,
    channelFlagPrivate: Command.parseBoolean,
    channelBannerGfxUrl: Command.parseString,
    channelBannerMode: Command.parseNumber,
    secondsEmpty: Command.parseNumber,
    cgid: Command.parseString,
    sgid: Command.parseString,
    permid: Command.parseString,
    permvalue: Command.parseNumber,
    permnegated: Command.parseBoolean,
    permskip: Command.parseBoolean,
    permsid: Command.parseString,
    t: Command.parseNumber,
    id1: Command.parseString,
    id2: Command.parseString,
    p: Command.parseNumber,
    v: Command.parseNumber,
    n: Command.parseNumber,
    s: Command.parseNumber,
    reasonid: Command.parseString,
    reasonmsg: Command.parseString,
    ctid: Command.parseString,
    cfid: Command.parseString,
    targetmode: Command.parseNumber,
    target: Command.parseNumber,
    invokerid: Command.parseString,
    invokername: Command.parseString,
    invokeruid: Command.parseString,
    hash: Command.parseString,
    lastPos: Command.parseNumber,
    fileSize: Command.parseNumber,
    l: Command.parseString,
    path: Command.parseString,
    size: Command.parseNumber,
    clientftfid: Command.parseString,
    serverftfid: Command.parseString,
    currentSpeed: Command.parseNumber,
    averageSpeed: Command.parseNumber,
    runtime: Command.parseNumber,
    sizedone: Command.parseNumber,
    sender: Command.parseNumber,
    status: Command.parseNumber,
    ftkey: Command.parseString,
    port: Command.parseNumber,
    proto: Command.parseNumber,
    datetime: Command.parseNumber,
    hostTimestampUtc: Command.parseNumber,
    instanceUptime: Command.parseNumber,
    virtualserversRunningTotal: Command.parseNumber,
    virtualserversTotalChannelsOnline: Command.parseNumber,
    virtualserversTotalClientsOnline: Command.parseNumber,
    virtualserversTotalMaxclients: Command.parseNumber,
    serverinstanceDatabaseVersion: Command.parseNumber,
    serverinstanceFiletransferPort: Command.parseNumber,
    serverinstanceServerqueryMaxConnectionsPerIp: Command.parseNumber,
    serverinstanceMaxDownloadTotalBandwidth: Command.parseNumber,
    serverinstanceMaxUploadTotalBandwidth: Command.parseNumber,
    serverinstanceGuestServerqueryGroup: Command.parseNumber,
    serverinstancePendingConnectionsPerIp: Command.parseNumber,
    serverinstancePermissionsVersion: Command.parseNumber,
    serverinstanceServerqueryFloodBanTime: Command.parseNumber,
    serverinstanceServerqueryFloodCommands: Command.parseNumber,
    serverinstanceServerqueryFloodTime: Command.parseNumber,
    serverinstanceTemplateChanneladminGroup: Command.parseString,
    serverinstanceTemplateChanneldefaultGroup: Command.parseString,
    serverinstanceTemplateServeradminGroup: Command.parseNumber,
    serverinstanceTemplateServerdefaultGroup: Command.parseString,
    msgid: Command.parseString,
    timestamp: Command.parseNumber,
    cluid: Command.parseString,
    subject: Command.parseString,
    message: Command.parseString,
    version: Command.parseString,
    build: Command.parseNumber,
    platform: Command.parseString,
    name: Command.parseString,
    token: Command.parseString,
    tokencustomset: Command.parseRecursive,
    value: Command.parseString,
    banid: Command.parseString,
    id: Command.parseString,
    msg: Command.parseString,
    extraMsg: Command.parseString,
    failedPermid: Command.parseString,
    ident: Command.parseString,
    ip: Command.parseString,
    nickname: Command.parseString,
    uid: Command.parseString,
    desc: Command.parseString,
    pwClear: Command.parseString,
    start: Command.parseNumber,
    end: Command.parseNumber,
    tcid: Command.parseString,
    permname: Command.parseString,
    permdesc: Command.parseString,
    tokenType: Command.parseNumber,
    tokenCustomset: Command.parseRecursive,
    token1: Command.parseString,
    token2: Command.parseString,
    tokenId1: Command.parseString,
    tokenId2: Command.parseString,
    tokenCreated: Command.parseNumber,
    tokenDescription: Command.parseString,
    flagRead: Command.parseBoolean,
    tcldbid: Command.parseString,
    tname: Command.parseString,
    fcldbid: Command.parseString,
    fname: Command.parseString,
    mytsid: Command.parseString,
    lastnickname: Command.parseString,
    created: Command.parseNumber,
    duration: Command.parseNumber,
    invokercldbid: Command.parseString,
    enforcements: Command.parseNumber,
    reason: Command.parseString,
    type: Command.parseNumber,
    iconid: Command.parseString,
    savedb: Command.parseNumber,
    namemode: Command.parseNumber,
    nModifyp: Command.parseNumber,
    nMemberAddp: Command.parseNumber,
    nMemberRemovep: Command.parseNumber,
    sortid: Command.parseString,
    count: Command.parseNumber,
    salt: Command.parseString,
    snapshot: Command.parseString,
    apikey: Command.parseString,
    scope: Command.parseString,
    timeLeft: Command.parseNumber,
    createdAt: Command.parseNumber,
    expiresAt: Command.parseNumber
  }
}