###
Doom map editing.

Does not support nodebuilding
yet.
###
BufferWriter = require("buffer-utils").BufferWriter
Bitfield = require("bitfield")

isClockwise = (positions) ->
    edges = []

    for i1 in [0..positions.length - 1]
        if i1 is positions.length - 1
            i2 = 0

        else
            i2 = i1 + 1

        edges.push( (positions[i2][0] - positions[i1][0]) * (positions[i2][1] + positions[i1][1]) )

    return edges.reduce((x, y) -> x + y) > 0

writeChars = (st, bw, cap, maxLen) ->
    if not cap? then cap = 0
    if not maxLen? then maxLen = st.toString('ascii').length

    if cap > 0
        padding = "\x00".repeat(cap - st.length)

    else
        padding = ""

    for c in st.slice(0, maxLen).toString() + padding
        data = (new Buffer(c, 'ascii'))
        bw.writeUInt8(data.readUInt8())

class WSector
    constructor: (@floorHeight, @ceilHeight, @floorTex, @ceilTex, @lighting, @special, @tag, @index) ->
        if not @floorHeight? then @floorHeight = 0
        if not @ceilHeight? then @ceilHeight = 128
        if not @floorTex? then @floorTex = "FLAT1" else @floorTex = @floorTex.toString('ascii')
        if not @ceilTex? then @ceilTex = "FLAT2" else @ceilTex = @ceilTex.toString('ascii')
        if not @lighting? then @lighting = 192
        if not @special? then @special = 0
        if not @tag? then @tag = 0

    write: (bw) =>
        bw
            .writeInt16LE(@floorHeight)
            .writeInt16LE(@ceilHeight)

        writeChars(@floorTex, bw, 8)
        writeChars(@ceilTex, bw, 8)

        bw
            .writeInt16LE(@lighting)
            .writeUInt16LE(@special)
            .writeUInt16LE(@tag)

        return @

defaultFlags = new Bitfield(16)
defaultFlags.set(0)
defaultFlags.set(1)
defaultFlags.set(2)
defaultFlags.set(8)
defaultFlags.set(9)
defaultFlags.set(10)

defaultFlags = defaultFlags.buffer.readUInt16LE()

class WThing
    constructor: (@xpos, @ypos, @angle, @type, @flags) ->
        if not @flags? then @flags = defaultFlags
        if not @angle? then @angle = 0

    write: (bw) =>
        bw
            .writeInt16LE(@xpos)
            .writeInt16LE(@ypos)
            .writeUInt16LE(@angle)
            .writeUInt16LE(@type)
            .writeUInt16LE(@flags)

        return @

class WSidedef
    constructor: (@xoffs, @yoffs, @uptex, @midtex, @lowtex, @frontSector, @index) ->
        if @frontSector instanceof WSector then @frontSector = @frontSector.index
        if not @frontSector? then @frontSector = -1

        if not @midtex? then @midtex = "STARTAN2" # MWAHAHAHAHAHA
        if not @uptex? then @uptex = "-"
        if not @lowtex? then @lowtex = "-"
        if not @xoffs? then @xoffs = 0
        if not @yoffs? then @yoffs = 0

    write: (bw) =>
        bw
            .writeInt16LE(@xoffs)
            .writeInt16LE(@yoffs)

        writeChars(@uptex, bw, 8)
        writeChars(@lowtex, bw, 8)
        writeChars(@midtex, bw, 8)

        bw.writeInt16LE(@frontSector)

        return @

class WLinedef
    constructor: (@begin, @end, @flags, @linetype, @tag, @frontSidedef, @backSidedef) ->
        if @begin instanceof WVertex then @begin = @begin.index
        if @end instanceof WVertex then @end = @end.index

        if @frontSidedef instanceof WSidedef then @frontSidedef = @frontSidedef.index
        if @backSidedef instanceof WSidedef then @backSidedef = @backSidedef.index

        if not @frontSidedef? then @frontSidedef = 0xFFFF
        if not @backSidedef? then @backSidedef = 0xFFFF

        if not @flags? then @flags = (if @backSidedef isnt 0xFFFF then 0b100 else 0b001)

        console.log(@index, @frontSidedef)

    write: (bw) =>
        bw
            .writeUInt16LE(@begin)
            .writeUInt16LE(@end)
            .writeUInt16LE(@flags)
            .writeUInt16LE(@linetype)
            .writeUInt16LE(@tag)
            .writeUInt16LE(@frontSidedef)
            .writeUInt16LE(@backSidedef)

        return @
        
class WVertex
    constructor: (@x, @y, @index) ->

    write: (bw) =>
        bw
            .writeInt16LE(@x)
            .writeInt16LE(@y)

        return @

    distanceTo: (v2) =>
        Math.sqrt(Math.abs(Math.pow(v2?.x - @x, 2) + Math.pow(v2?.y - @y, 2)))

class MapEdit
    constructor: (@mapName) ->
        @vertexes = []
        @linedefs = []
        @sidedefs = []
        @things = []
        @sectors = []

    @read: (wad, pos) ->
        # wip

        m = new MapEdit(wad.lumps[pos].name)
        return m

    toLumps: (wad) =>
        wad.addLump(@mapName)

        vb = new BufferWriter()

        @things.forEach((s) -> s.write(vb))
        wad.addLump("THINGS", vb.getContents())

        @linedefs.forEach((l) -> l.write(vb))
        wad.addLump("LINEDEFS", vb.getContents())

        @sidedefs.forEach((s) -> s.write(vb))
        wad.addLump("SIDEDEFS", vb.getContents())

        @vertexes.forEach((v) -> v.write(vb))
        data = vb.getContents()
        wad.addLump("VERTEXES", data)

        @sectors.forEach((v) -> v.write(vb))
        wad.addLump("SECTORS", vb.getContents())

        return @ # for chaining

    addVertex: (x, y) =>
        @vertexes.push(new WVertex(x, y, @vertexes.length))

        return @ # for chaining

    addThing: (x, y, type, angle, flags) =>
        @things.push(new WThing(x, y, angle, type, flags))

        return @ # for chaining

    buildSector: (positions, sectorData, lineData) =>
        if not sectorData? then sectorData = {}
        if not lineData? then lineData = {}

        newv = []
        newlines = []
        newpos = @vertexes.length

        for p in positions
            if p in @vertexes.map((v) -> [v.x, v.y])
                v = @vertexes[@vertexes.map((v2) -> [v2.x, v2.y]).indexOf(p)]

            else
                v = new WVertex(p[0], p[1], @vertexes.length)

                console.log([v.x, v.y], @vertexes
                    .filter((v2) ->
                        dist = v.distanceTo(v2)
                        return dist < 16
                    )
                    .map((v2) -> [v2.x, v2.y]))

                @vertexes.push(v)

            newv.push(v)
        
        vi = 0

        sector = new WSector(
            sectorData.floorHeight,
            sectorData.ceilHeight,
            sectorData.floorTex,
            sectorData.ceilTex,
            sectorData.light,
            sectorData.special,
            sectorData.tag,
            @sectors.index
        )

        @sectors.push(sector)

        for v1 in newv
            v2 = newv[if vi < newv.length - 1 then vi + 1 else 0]

            side = new WSidedef(lineData.xOff, lineData.yOff, lineData.upTex, lineData.midTex, lineData.lowTex, sector, @sidedefs.length)
            @sidedefs.push(side)

            if v1.index >= newpos and v2.index >= newpos
                line = new WLinedef(v1, v2, lineData.flags, lineData.lineType, lineData.tag, side, null)
                @linedefs.push(line)

            else
                for l in @linedefs
                    if l.begin == v1.index and l.end == v2.index
                        line = l
                        break

                if line.backSidedef < 0xFFFF # testing stuff
                    line.frontSidedef = side

                else
                    line.backSidedef = side

            vi++

        return @

module.exports = MapEdit