Don't overflow network

Don't overflow network

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

PPM2.PLAYER_VIEW_OFFSET = 64 * .7
PPM2.PLAYER_VIEW_OFFSET_DUCK = 28 * 1.2

hook.Add 'PostPlayerDeath', 'PPM2.Hooks', =>
    return if not @GetPonyData()
    data = @GetPonyData()
    bgController = data\GetBodygroupController()
    rag = @GetRagdollEntity()
    return if not IsValid(rag)
    bgController\MergeModels(rag) if bgController.MergeModels

hook.Add 'EntityTakeDamage', 'PPM2.Hooks', (ent, dmg) ->
    do
        self = ent
        if @IsPlayer()
            @__ppm2_last_hurt_anim = @__ppm2_last_hurt_anim or 0
            if @__ppm2_last_hurt_anim < CurTime()
                @__ppm2_last_hurt_anim = CurTime() + 1
                net.Start('PPM2.DamageAnimation', true)
                net.WriteEntity(@)
                net.Broadcast()
    do
        self = dmg\GetAttacker()
        if @IsPlayer() and IsValid(ent) and (ent\IsNPC() or ent\IsPlayer())
            @__ppm2_last_anger_anim = @__ppm2_last_anger_anim or 0
            if @__ppm2_last_anger_anim < CurTime()
                @__ppm2_last_anger_anim = CurTime() + 1
                net.Start('PPM2.AngerAnimation', true)
                net.WriteEntity(@)
                net.Broadcast()

killGrin = =>
    return if not IsValid(@)
    return if not @IsPlayer()
    @__ppm2_grin_hurt_anim = @__ppm2_grin_hurt_anim or 0
    return if @__ppm2_grin_hurt_anim > CurTime()
    @__ppm2_grin_hurt_anim = CurTime() + 1
    net.Start('PPM2.KillAnimation', true)
    net.WriteEntity(@)
    net.Broadcast()

hook.Add 'OnNPCKilled', 'PPM2.Hooks', (npc = NULL, attacker = NULL, weapon = NULL) => killGrin(attacker)
hook.Add 'DoPlayerDeath', 'PPM2.Hooks', (ply = NULL, attacker = NULL) => killGrin(attacker)

hook.Add 'PlayerSpawn', 'PPM2.Hooks', =>
    for ent in *ents.GetAll()
        if ent.isPonyPropModel and ent.manePlayer == @
            ent\Remove()

    timer.Simple 0.5, ->
        return unless @IsValid()
        return unless @IsPony()

        @SetViewOffset(Vector(0, 0, PPM2.PLAYER_VIEW_OFFSET))
        @SetViewOffsetDucked(Vector(0, 0, PPM2.PLAYER_VIEW_OFFSET_DUCK))
        if @GetPonyData()
            @GetPonyData()\ApplyBodygroups()
            net.Start('PPM2.PlayerRespawn')
            net.WriteEntity(@)
            net.Broadcast()
            return

        timer.Simple 0.5, ->
            net.Start('PPM2.RequestPonyData')
            net.Send(@)

hook.Add 'PlayerInitialSpawn', 'PPM2.Hooks', =>
    timer.Simple 0.5, ->
        return unless @IsValid()
        return unless @IsPony()

        timer.Simple 10, ->
            net.Start('PPM2.RequestPonyData')
            net.Send(@)

do
    REQUIRE_CLIENTS = {}

    safeSendFunction = ->
        for ply, data in pairs REQUIRE_CLIENTS
            if not IsValid(ply)
                REQUIRE_CLIENTS[ply] = nil
                continue

            ent = table.remove(data, 1)
            if not ent
                REQUIRE_CLIENTS[ply] = nil
                continue

            data = ent\GetPonyData()
            continue if not data
            data\NetworkTo(ply)

    errorTrack = (err) ->
        print '[PPM2] Networking Error: ', err
        print debug.traceback()

    timer.Create 'PPM2.Require', 1, 0, -> xpcall(safeSendFunction, errorTrack)
    net.Receive 'PPM2.Require', (len = 0, ply = NULL) ->
        return if not IsValid(ply)
        REQUIRE_CLIENTS[ply] = for ent in *ents.GetAll()
            continue if ent == ply
            data = ent\GetPonyData()
            continue if not data
            ent

net.Receive 'PPM2.EditorStatus', (len = 0, ply = NULL) ->
    return if not IsValid(ply)
    ply\SetNWBool('PPM2.InEditor', net.ReadBool())

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

PPM2.PLAYER_VIEW_OFFSET = 64 * .7
PPM2.PLAYER_VIEW_OFFSET_DUCK = 28 * 1.2

hook.Add 'PostPlayerDeath', 'PPM2.Hooks', =>
    return if not @GetPonyData()
    data = @GetPonyData()
    bgController = data\GetBodygroupController()
    rag = @GetRagdollEntity()
    return if not IsValid(rag)
    bgController\MergeModels(rag) if bgController.MergeModels

hook.Add 'EntityTakeDamage', 'PPM2.Hooks', (ent, dmg) ->
    do
        self = ent
        if @IsPlayer()
            @__ppm2_last_hurt_anim = @__ppm2_last_hurt_anim or 0
            if @__ppm2_last_hurt_anim < CurTime()
                @__ppm2_last_hurt_anim = CurTime() + 1
                net.Start('PPM2.DamageAnimation', true)
                net.WriteEntity(@)
                net.Broadcast()
    do
        self = dmg\GetAttacker()
        if @IsPlayer() and IsValid(ent) and (ent\IsNPC() or ent\IsPlayer())
            @__ppm2_last_anger_anim = @__ppm2_last_anger_anim or 0
            if @__ppm2_last_anger_anim < CurTime()
                @__ppm2_last_anger_anim = CurTime() + 1
                net.Start('PPM2.AngerAnimation', true)
                net.WriteEntity(@)
                net.Broadcast()

killGrin = =>
    return if not IsValid(@)
    return if not @IsPlayer()
    @__ppm2_grin_hurt_anim = @__ppm2_grin_hurt_anim or 0
    return if @__ppm2_grin_hurt_anim > CurTime()
    @__ppm2_grin_hurt_anim = CurTime() + 1
    net.Start('PPM2.KillAnimation', true)
    net.WriteEntity(@)
    net.Broadcast()

hook.Add 'OnNPCKilled', 'PPM2.Hooks', (npc = NULL, attacker = NULL, weapon = NULL) => killGrin(attacker)
hook.Add 'DoPlayerDeath', 'PPM2.Hooks', (ply = NULL, attacker = NULL) => killGrin(attacker)

hook.Add 'PlayerSpawn', 'PPM2.Hooks', =>
    for ent in *ents.GetAll()
        if ent.isPonyPropModel and ent.manePlayer == @
            ent\Remove()

    timer.Simple 0.5, ->
        return unless @IsValid()
        return unless @IsPony()

        @SetViewOffset(Vector(0, 0, PPM2.PLAYER_VIEW_OFFSET))
        @SetViewOffsetDucked(Vector(0, 0, PPM2.PLAYER_VIEW_OFFSET_DUCK))
        if @GetPonyData()
            @GetPonyData()\ApplyBodygroups()
            net.Start('PPM2.PlayerRespawn')
            net.WriteEntity(@)
            net.Broadcast()
            return

        timer.Simple 0.5, ->
            net.Start('PPM2.RequestPonyData')
            net.Send(@)

hook.Add 'PlayerInitialSpawn', 'PPM2.Hooks', =>
    timer.Simple 0.5, ->
        return unless @IsValid()
        return unless @IsPony()

        timer.Simple 10, ->
            net.Start('PPM2.RequestPonyData')
            net.Send(@)

net.Receive 'PPM2.Require', (len = 0, ply = NULL) ->
    return if not IsValid(ply)
    for ent in *ents.GetAll()
        continue if ent == ply
        data = ent\GetPonyData()
        continue if not data
        data\NetworkTo(ply)

net.Receive 'PPM2.EditorStatus', (len = 0, ply = NULL) ->
    return if not IsValid(ply)
    ply\SetNWBool('PPM2.InEditor', net.ReadBool())


Za Midzu

Za Midzu

lapis = require "lapis"
config = (require "lapis.config").get!
content = require "content"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
import capture_errors, assert_error, yield_error from require "lapis.application"
safe_route = (fn) ->
    capture_errors {
        on_error: =>
            @app\error_handler self

        =>
            fn self
    }

class CSWeek extends lapis.Application
    layout: require "views.layout"

    error_handler: (context, code) =>
        code = code or 500
        context = context or self

        with context
            .m, err = content\get "error"
            if err then
                print "Could not load the error page (code #{code}): #{err}"
                .m = { }

            .m[code] = .m[code] or .m.default or { }
            .page_id = "error"
            .status = code
            .m.title = .m[code].title

        render: "error", status: code

    try_render: (template, context) =>
        context = context or self
        with context
            .nav = assert_error content\get "nav"
            .footer = assert_error content\get "footer"
        render: template

    @before_filter =>
        if #[n for n in *{'production-perftest', 'development-perftest'} when n == config._name] > 0
            after_dispatch ->
                print to_json(ngx.ctx.performance)

    [home: "/"]: safe_route =>
        @page_id = "home"
        @m = assert_error content\get "home"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "home", self

    [about: "/o-nedelji"]: safe_route =>
        @page_id = "about"
        @m = assert_error content\get "about"
        @app\try_render "about", self

    [lecturers: "/predavaci"]: safe_route =>
        @page_id = "lecturers"
        @m = assert_error content\get "lecturers_page"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "lecturers", self

    [lecturer: "/predavaci/:name"]: =>
        @lecturers_page = assert_error content\get "lecturers_page"
        @page_id = "lecturer"
        lecturers = assert_error content\get "lecturers"
        results = [l for l in *lecturers when l.id == @params.name]
        if #results > 1
            yield_error "Multiple lecturer id's found for id `#{@params.name}`"
        else if #results == 0
            @errors = "No such lecturer: `#{@params.name}`"
            @app\error_handler self, 404

        @m = results[1]
        @app\try_render "lecturer", self

    handle_404: =>
        @errors = "Route `#{self.req.parsed_url.path or 'unknown'}` not found"
        @app\error_handler self, 404

lapis = require "lapis"
config = (require "lapis.config").get!
content = require "content"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
import capture_errors, assert_error, yield_error from require "lapis.application"
safe_route = (fn) ->
    capture_errors {
        on_error: =>
            @app\error_handler self

        =>
            fn self
    }

class CSWeek extends lapis.Application
    layout: require "views.layout"

    error_handler: (context, code) =>
        code = code or 500
        context = context or self

        with context
            .m, err = content\get "error"
            if err then
                print "Could not load the error page (code #{code}): #{err}"
                .m = { }

            .m[code] = .m[code] or .m.default or { }
            .page_id = "error"
            .status = code
            .m.title = .m[code].title

        render: "error", status: code

    try_render: (template, context) =>
        context = context or self
        with context
            .nav = assert_error content\get "nav"
            .footer = assert_error content\get "footer"
        render: template

    @before_filter =>
        if #[n for n in *{'production-perftest', 'development-perftest'} when n == config._name] > 0
            after_dispatch ->
                print to_json(ngx.ctx.performance)

    [home: "/"]: safe_route =>
        @page_id = "home"
        @m = assert_error content\get "home"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "home", self

    [about: "/o-nedelji"]: safe_route =>
        @page_id = "about"
        @m = assert_error content\get "about"
        @app\try_render "about", self

    [lecturers: "/predavaci"]: safe_route =>
        @page_id = "lecturers"
        @m = assert_error content\get "lecturers_page"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "lecturers", self

    [lecturer: "/predavaci/:name"]: =>
        @page_id = "lecturer"
        @lecturers_page = assert_error content\get "lecturers_page"
        lecturers = assert_error content\get "lecturers"
        results = [l for l in *lecturers when l.id == @params.name]
        if #results > 1
            yield_error "Multiple lecturer id's found for id `#{@params.name}`"
        else if #results == 0
            @errors = "No such lecturer: `#{@params.name}`"
            @app\error_handler self, 404

        @m = results[1]
        @app\try_render "lecturer", self

    handle_404: =>
        @errors = "Route `#{self.req.parsed_url.path or 'unknown'}` not found"
        @app\error_handler self, 404


textutils: fix !nocolor command to use util

Former-commit-id: f69149c735b0a384a3bdcd14306a3953e30abc3c [formerly 71105b893388d225c6d2d067149acc6a64ef3f2a]
Former-commit-id: 03c61d1dd05f212ac25abe03de761779c3f1a9fe

PRIVMSG:
  '^%pupper (.+)$': (source, destination, arg) =>
    say arg\upper!
  '^%plower (.+)$': (source, destination, arg) =>
    say arg\lower!
  '^%preverse (.+)$': (source, destination, arg) =>
    say arg\reverse!
  '^%plen (.+)$': (source, destination, arg) =>
    say tostring(arg\len!)
  '^%pnicks$': (source, destination) =>
    say table.concat([ivar2.util.nonickalert(ivar2.channels[destination].nicks, n) for n,k in pairs(ivar2.channels[destination].nicks)], ' ')
  '^%prandom (.+)$': (source, destination, arg) =>
    words = [word for word in arg\gmatch('%S+')]
    say words[math.random(1, #words)]
  '^%pbold (.+)$': (source, destination, arg) =>
    say ivar2.util.bold(arg)
  '^%punderline (.+)$': (source, destination, arg) =>
    say ivar2.util.underline(arg)
  '^%pitalic (.+)$': (source, destination, arg) =>
    say ivar2.util.italic(arg)
  '^%pinvert (.+)$': (source, destination, arg) =>
    say ivar2.util.reverse(arg)
  '^%ptrim (.+)$': (source, destination, arg) =>
    say ivar2.util.trim(arg)
  '^%pcolor ([0-9]+) (.+)$': (source, destination, color, arg) =>
    say ivar2.util.color(arg, color)
  '^%pfirst (.+)$': (source, destination, arg) =>
    say ivar2.util.split(arg, '%s')[1]
  '^%psplit (.-) (.*)$': (source, destination, arg, args) =>
    say table.concat(ivar2.util.split(args, arg), ' ')
  '^%preplace (.-) (.-) (.*)$': (source, destination, pat, repl, arg) =>
    new, n = string.gsub(arg, pat, repl)
    say(new)
  '^%pnocolor (.*)$': (source, destination, arg) =>
    say ivar2.util.stripformatting(arg)
  '^%pstutter (.*)$': (source, destination, arg) =>
    -- Stutter by anders from luabot
    s_senpai = 0.65
    say arg\gsub("(%a[%w%p]+)", (w) =>
      if math.random! >= s_senpai
        return (w\sub(1, 1).."-")\rep(math.random(1, 3))..w
      else
        return w)
  '^%prot13 (.+)$': (source, destination, arg) =>
    say ivar2.util.rot13(arg)

PRIVMSG:
  '^%pupper (.+)$': (source, destination, arg) =>
    say arg\upper!
  '^%plower (.+)$': (source, destination, arg) =>
    say arg\lower!
  '^%preverse (.+)$': (source, destination, arg) =>
    say arg\reverse!
  '^%plen (.+)$': (source, destination, arg) =>
    say tostring(arg\len!)
  '^%pnicks$': (source, destination) =>
    say table.concat([ivar2.util.nonickalert(ivar2.channels[destination].nicks, n) for n,k in pairs(ivar2.channels[destination].nicks)], ' ')
  '^%prandom (.+)$': (source, destination, arg) =>
    words = [word for word in arg\gmatch('%S+')]
    say words[math.random(1, #words)]
  '^%pbold (.+)$': (source, destination, arg) =>
    say ivar2.util.bold(arg)
  '^%punderline (.+)$': (source, destination, arg) =>
    say ivar2.util.underline(arg)
  '^%pitalic (.+)$': (source, destination, arg) =>
    say ivar2.util.italic(arg)
  '^%pinvert (.+)$': (source, destination, arg) =>
    say ivar2.util.reverse(arg)
  '^%ptrim (.+)$': (source, destination, arg) =>
    say ivar2.util.trim(arg)
  '^%pcolor ([0-9]+) (.+)$': (source, destination, color, arg) =>
    say ivar2.util.color(arg, color)
  '^%pfirst (.+)$': (source, destination, arg) =>
    say ivar2.util.split(arg, '%s')[1]
  '^%psplit (.-) (.*)$': (source, destination, arg, args) =>
    say table.concat(ivar2.util.split(args, arg), ' ')
  '^%preplace (.-) (.-) (.*)$': (source, destination, pat, repl, arg) =>
    new, n = string.gsub(arg, pat, repl)
    say(new)
  '^%pnocolor (.*)$': (source, destination, arg) =>
    new, n = string.gsub(arg, '\002', '')
    new, n = string.gsub(new, '\003[0-9]+(,[0-9]+)?', '')
    say(new)
  '^%pstutter (.*)$': (source, destination, arg) =>
    -- Stutter by anders from luabot
    s_senpai = 0.65
    say arg\gsub("(%a[%w%p]+)", (w) =>
      if math.random! >= s_senpai
        return (w\sub(1, 1).."-")\rep(math.random(1, 3))..w
      else
        return w)
  '^%prot13 (.+)$': (source, destination, arg) =>
    say ivar2.util.rot13(arg)


Fix invalid localization access in options

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

NAME, T = ...

{database: db, localization: L, :Logger, xp_manager: xp} = T

local panel

checkbox = (label, description, on_click) ->
    with check = CreateFrame 'CheckButton', "#{NAME}Check#{label}", panel, 'InterfaceOptionsCheckButtonTemplate'
        \SetScript 'OnClick', =>
            PlaySound @GetChecked! and 'igMainMenuOptionCheckBoxOn' or 'igMainMenuOptionCheckBoxOff'
            on_click @, @GetChecked! and true or false
        check.label = with _G["#{check\GetName!}Text"]
            \SetText label
        .tooltipText = label
        .tooltipRequirement = description

button = (text, tooltip, on_click) ->
    with CreateFrame 'Button', nil, panel, 'UIPanelButtonTemplate'
        \SetText text
        .tooltipText = tooltip
        \SetScript 'OnClick', =>
            on_click @
        \SetHeight 24

panel = with CreateFrame 'Frame'
    .name = NAME
    \Hide!
    \SetScript 'OnShow', =>
        title = with @CreateFontString nil, 'ARTWORK', 'GameFontNormalLarge'
            \SetPoint 'TOPLEFT', 16, -16
            \SetText NAME

        minimap = checkbox L.opt_minimap!, L.opt_minimap_desc!, (checked) =>
            T.broker\set_minimap checked!
        minimap\SetPoint 'TOPLEFT', title, 'BOTTOMLEFT', -2, -16

        logging = checkbox L.opt_logging!, L.opt_logging_desc!, (checked) =>
            db\set 'log', checked
        logging\SetPoint 'TOPLEFT', minimap, 'BOTTOMLEFT', 0, -8

        debug = checkbox L.opt_debug!, L.opt_debug_desc!, (checked) =>
            T\set_debug checked
        debug\SetPoint 'TOPLEFT', logging, 'BOTTOMLEFT', 0, -8

        local update_lld

        loglevel_dropdown = with CreateFrame 'Frame', "#{NAME}LogLevels", @, 'UIDropDownMenuTemplate'
            \SetPoint 'TOPLEFT', debug, 'BOTTOMLEFT', -15, -10
            .initialize = ->
                for name, value in pairs Logger.levels
                    UIDropDownMenu_AddButton
                        text: name
                        value: value
                        func: =>
                            db\set 'log.level', @value
                            update_lld! if update_lld
                        checked: value == db 'log.level', Logger.levels.INFO
            .text = _G["#{\GetName!}Text"]

        update_lld = ->
            loglevel_dropdown.text\SetText L.opt_loglevel Logger\level_to_prefix db('log.level', Logger.levels.info)

        request = with button L.opt_request!, L.opt_request_desc!, => xp\request_xp!
            \SetWidth 150
            \SetPoint 'TOPLEFT', loglevel_dropdown, 'BOTTOMLEFT', 17, -25

        send = with button L.opt_send!, L.opt_send_desc!, => xp\send_xp!
            \SetWidth 150
            \SetPoint 'TOPLEFT', request, 'TOPRIGHT', 8, 0

        init = ->
            minimap\SetChecked T.broker\is_minimap_enabled!
            logging\SetChecked db('log', true)
            debug\SetChecked T\is_debug_enabled!
            update_lld!

        reset = ->
            T.broker\reset_minimap!
            db\reset 'log'
            T\reset_debug!
            db\reset 'log.level'
            init!

        init!

        @default = ->
            reset!

        @SetScript 'OnShow', =>
            init!

InterfaceOptions_AddCategory panel

-- Copyright (c) 2014 by Adam Hellberg <sharparam@sharparam.com>.
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

NAME, T = ...

{database: db, localization: L, :Logger, xp_manager: xp} = T

local panel

checkbox = (label, description, on_click) ->
    with check = CreateFrame 'CheckButton', "#{NAME}Check#{label}", panel, 'InterfaceOptionsCheckButtonTemplate'
        \SetScript 'OnClick', =>
            PlaySound @GetChecked! and 'igMainMenuOptionCheckBoxOn' or 'igMainMenuOptionCheckBoxOff'
            on_click @, @GetChecked! and true or false
        check.label = with _G["#{check\GetName!}Text"]
            \SetText label
        .tooltipText = label
        .tooltipRequirement = description

button = (text, tooltip, on_click) ->
    with CreateFrame 'Button', nil, panel, 'UIPanelButtonTemplate'
        \SetText text
        .tooltipText = tooltip
        \SetScript 'OnClick', =>
            on_click @
        \SetHeight 24

panel = with CreateFrame 'Frame'
    .name = NAME
    \Hide!
    \SetScript 'OnShow', =>
        title = with @CreateFontString nil, 'ARTWORK', 'GameFontNormalLarge'
            \SetPoint 'TOPLEFT', 16, -16
            \SetText NAME

        minimap = checkbox L.opt_minimap, L.opt_minimap_desc, (checked) =>
            T.broker\set_minimap checked!
        minimap\SetPoint 'TOPLEFT', title, 'BOTTOMLEFT', -2, -16

        logging = checkbox L.opt_logging, L.opt_logging_desc!, (checked) =>
            db\set 'log', checked
        logging\SetPoint 'TOPLEFT', minimap, 'BOTTOMLEFT', 0, -8

        debug = checkbox L.opt_debug!, L.opt_debug_desc!, (checked) =>
            T\set_debug checked
        debug\SetPoint 'TOPLEFT', logging, 'BOTTOMLEFT', 0, -8

        local update_lld

        loglevel_dropdown = with CreateFrame 'Frame', "#{NAME}LogLevels", @, 'UIDropDownMenuTemplate'
            \SetPoint 'TOPLEFT', debug, 'BOTTOMLEFT', -15, -10
            .initialize = ->
                for name, value in pairs Logger.levels
                    UIDropDownMenu_AddButton
                        text: name
                        value: value
                        func: =>
                            db\set 'log.level', @value
                            update_lld! if update_lld
                        checked: value == db 'log.level', Logger.levels.INFO
            .text = _G["#{\GetName!}Text"]

        update_lld = ->
            loglevel_dropdown.text\SetText L.opt_loglevel Logger\level_to_prefix db('log.level', Logger.levels.info)

        request = with button L.opt_request!, L.opt_request_desc!, => xp\request_xp!
            \SetWidth 150
            \SetPoint 'TOPLEFT', loglevel_dropdown, 'BOTTOMLEFT', 17, -25

        send = with button L.opt_send!, L.opt_send_desc!, => xp\send_xp!
            \SetWidth 150
            \SetPoint 'TOPLEFT', request, 'TOPRIGHT', 8, 0

        init = ->
            minimap\SetChecked T.broker\is_minimap_enabled!
            logging\SetChecked db('log', true)
            debug\SetChecked T\is_debug_enabled!
            update_lld!

        reset = ->
            T.broker\reset_minimap!
            db\reset 'log'
            T\reset_debug!
            db\reset 'log.level'
            init!

        init!

        @default = ->
            reset!

        @SetScript 'OnShow', =>
            init!

InterfaceOptions_AddCategory panel


unpack to table.unpack

unpack to table.unpack

parse = require'moonscript.parse'
compile = require'moonscript.compile'
ln = require'linenoise'
inspect = require'inspect'
import remove, insert, concat from table

printerr = (...) -> io.stderr\write "#{concat {...}, "\t"}\n"

prompt =
	p: ">"
	deepen: => @p = "? "
	reset: => @p = ">"

-- MoonScript transpiler
to_lua = (code) ->
	tree, err = parse.string code

	return nil, err if err

	lua_code, err, pos = compile.tree tree

	unless lua_code
		nil, compile.format_error err, pos, code
	else lua_code

-- Lua evaluator & printer
fnwrap = (code) -> "return function(__newenv) local _ENV = setmetatable(__newenv, {__index = _ENV}) #{code} end"

evalprint = (env, lua_code) ->
	lua_code = if vardec = lua_code\match"^local%s+(.*)$"
			if exportFnCl = vardec\match "^%w+%s+(.*)$"
				if exportFnCl\match "^="
					"#{lua_code\match"local%s+(%w+)"} #{exportFnCl}"
				else exportFnCl
			else vardec
		elseif lua_code\match"__class"
			lua_code = lua_code\gsub "^local%s+", "export" , "1"
		else lua_code

	luafn, err = load fnwrap(lua_code), "tmp"

	return printerr err if err

	result = {pcall luafn!, env}

	ok = remove result, 1

	unless ok then printerr result[1]
	else
		if #result > 0
			print (inspect result)\match"^%s*{%s*(.*)%s*}%s*%n?%s*$"
			table.unpack result

---- tab completion
cndgen = (env) ->  (line) ->
	if i1 = line\find '[.\\%w_]+$' -- if completable
		with res = {}
			front = line\sub(1, i1 - 1)
			partial = line\sub i1
			prefix, last = partial\match '(.-)([^.\\]*)$'
			t, all = env

			if #prefix > 0 -- tbl.ky or not
				P = prefix\sub(1, -2)
				all = last == ''

				for w in P\gmatch '[^.\\]+'
					t = t[w]

					return unless t

			prefix = front .. prefix

			append_candidates = (t) ->
				if type(t) == 'table'
					table.insert(res, prefix .. k) for k in pairs t when all or k\sub(1, #last) == last

			append_candidates t

			if mt = getmetatable t then append_candidates mt.__index

compgen = (env) ->
	candidates = cndgen env

	(c, s) -> if cc = candidates s
		ln.addcompletion c, name for name in *cc

string.match_if_fncls = =>
	@\match"[-=]>$" or
	@\match"class%s*$" or
	@\match"class%s+%w+$" or
	@\match"class%s+extends%s+%w+%s*$" or
	@\match"class%s+%w+%s+extends%s+%w+%s*$"

-- for busted unit test, repl is sepalated with `get_line` and `replgen`
-- and using the former for the test, the latter is only needed by `repl`.

get_line = ->
	with line = ln.linenoise prompt.p .. " "
		if line and line\match '%S' then ln.historyadd line

replgen = (get_line) -> (env = {}, _ENV = _ENV) ->
	block = {}

	ln.setcompletion compgen _ENV

	while true
		line = get_line!

		unless line then break
		elseif #line < 1 then continue

		-- if line\match"^:"
			-- (require'moor.replcmd') line
			-- continue

		is_fncls, lua_code, err =  if line\match_if_fncls!
			true
		else
			false, to_lua line

		if lua_code and not err
			evalprint env, lua_code
		elseif is_fncls or err\match "^Failed to parse"
			insert block, line

			prompt.reset with prompt
				\deepen!
				while line and #line > 0
					line = get_line!
					insert block, " #{line}"

			lua_code, err = to_lua concat block, "\n"

			if lua_code then evalprint env, lua_code

			block = {}

		if err
			printerr err

	env

-- this is main repl
repl = replgen get_line

setmetatable {:printerr, :to_lua, :evalprint, :replgen, :repl}, __call: (env, _ENV) => repl env, _ENV

parse = require'moonscript.parse'
compile = require'moonscript.compile'
ln = require'linenoise'
inspect = require'inspect'
import remove, insert, concat from table

printerr = (...) -> io.stderr\write "#{concat {...}, "\t"}\n"

prompt =
	p: ">"
	deepen: => @p = "? "
	reset: => @p = ">"

-- MoonScript transpiler
to_lua = (code) ->
	tree, err = parse.string code

	return nil, err if err

	lua_code, err, pos = compile.tree tree

	unless lua_code
		nil, compile.format_error err, pos, code
	else lua_code

-- Lua evaluator & printer
fnwrap = (code) -> "return function(__newenv) local _ENV = setmetatable(__newenv, {__index = _ENV}) #{code} end"

evalprint = (env, lua_code) ->
	lua_code = if vardec = lua_code\match"^local%s+(.*)$"
			if exportFnCl = vardec\match "^%w+%s+(.*)$"
				if exportFnCl\match "^="
					"#{lua_code\match"local%s+(%w+)"} #{exportFnCl}"
				else exportFnCl
			else vardec
		elseif lua_code\match"__class"
			lua_code = lua_code\gsub "^local%s+", "export" , "1"
		else lua_code

	luafn, err = load fnwrap(lua_code), "tmp"

	return printerr err if err

	result = {pcall luafn!, env}

	ok = remove result, 1

	unless ok then printerr result[1]
	else
		if #result > 0
			print (inspect result)\match"^%s*{%s*(.*)%s*}%s*%n?%s*$"
			unpack result

---- tab completion
cndgen = (env) ->  (line) ->
	if i1 = line\find '[.\\%w_]+$' -- if completable
		with res = {}
			front = line\sub(1, i1 - 1)
			partial = line\sub i1
			prefix, last = partial\match '(.-)([^.\\]*)$'
			t, all = env

			if #prefix > 0 -- tbl.ky or not
				P = prefix\sub(1, -2)
				all = last == ''

				for w in P\gmatch '[^.\\]+'
					t = t[w]

					return unless t

			prefix = front .. prefix

			append_candidates = (t) ->
				if type(t) == 'table'
					table.insert(res, prefix .. k) for k in pairs t when all or k\sub(1, #last) == last

			append_candidates t

			if mt = getmetatable t then append_candidates mt.__index

compgen = (env) ->
	candidates = cndgen env

	(c, s) -> if cc = candidates s
		ln.addcompletion c, name for name in *cc

string.match_if_fncls = =>
	@\match"[-=]>$" or
	@\match"class%s*$" or
	@\match"class%s+%w+$" or
	@\match"class%s+extends%s+%w+%s*$" or
	@\match"class%s+%w+%s+extends%s+%w+%s*$"

-- for busted unit test, repl is sepalated with `get_line` and `replgen`
-- and using the former for the test, the latter is only needed by `repl`.

get_line = ->
	with line = ln.linenoise prompt.p .. " "
		if line and line\match '%S' then ln.historyadd line

replgen = (get_line) -> (env = {}, _ENV = _ENV) ->
	block = {}

	ln.setcompletion compgen _ENV

	while true
		line = get_line!

		unless line then break
		elseif #line < 1 then continue

		-- if line\match"^:"
			-- (require'moor.replcmd') line
			-- continue

		is_fncls, lua_code, err =  if line\match_if_fncls!
			true
		else
			false, to_lua line

		if lua_code and not err
			evalprint env, lua_code
		elseif is_fncls or err\match "^Failed to parse"
			insert block, line

			prompt.reset with prompt
				\deepen!
				while line and #line > 0
					line = get_line!
					insert block, " #{line}"

			lua_code, err = to_lua concat block, "\n"

			if lua_code then evalprint env, lua_code

			block = {}

		if err
			printerr err

	env

-- this is main repl
repl = replgen get_line

setmetatable {:printerr, :to_lua, :evalprint, :replgen, :repl}, __call: (env, _ENV) => repl env, _ENV


plugins/output: Fix issue with untracked user statuses