-- BEGIN kst/parsers/ksml/coal
local VERSION = "COAL"

local color = {
    ["WHITE"] = colors.white,
    ["ORANGE"] = colors.orange,
    ["MAGENTA"] = colors.magenta,
    ["LIGHTBLUE"] = colors.lightBlue,
    ["YELLOW"] = colors.yellow,
    ["LIME"] = colors.lime,
    ["PINK"] = colors.pink,
    ["GRAY"] = colors.gray, -- gray
    ["GREY"] = colors.gray, -- grey
    ["LIGHTGRAY"] = colors.lightGray, --gray
    ["LIGHTGREY"] = colors.lightGray, --grey
    ["CYAN"] = colors.cyan,
    ["PURPLE"] = colors.purple,
    ["BLUE"] = colors.blue,
    ["BROWN"] = colors.brown,
    ["GREEN"] = colors.green,
    ["RED"] = colors.red, 
    ["BLACK"] = colors.black 
}

local function wrap(word,width) -- width might not be provided
    local lines = {""}
    width = arg or term.getSize()~= 51 and term.getSize() or 50
    xmin = term.getCursorPos()
    xmax = xmin+width
    for wordspace in word:gmatch("%w+%s+") do
        if #(lines[#lines]..wordspace)>width then
            lines[#lines+1] = wordspace
        else
            lines[#lines] = lines[#lines]..wordspace
        end
    end
    for k,v in pairs(lines) do
        print(v)
        local _,y = term.getCursorPos()
        term.setCursorPos(xmin,y)
    end
end

if not oldSetTColor then
    oldSetTColor = term.setTextColor
end
if not oldSetBColor then
    oldSetBColor = term.setBackgroundColor
end

local tColor = {}
local bColor = {}

_G.term.setTextColor = function(color)
    if colors[color] or type(color) == "number" then
        table.insert(tColor,color)
    end
    return oldSetTColor(color)
end

_G.term.getTextColor = function()
    return tColor[#tColor] or colors.black
end

_G.term.setBackgroundColor = function(color)
    if colors[color] or type(color) == "number" then
        table.insert(bColor,color)
    end
    return oldSetBColor
end

_G.term.getBackgroundColor = function()
    return bColor[#bColor] or colors.black
end

tags = {
    ["closing"] = { -- anything with a closing tag
        ["C"] = function(arg,content)
            --go through colors here
            local oldColor = term.getTextColor()
            if color[arg] then
                term.setTextColor(color[arg])
            end
            for i=1, #content do
                if type(content[i]) == "string" then
                    term.write(content[i])
                elseif type(content[i]) == "table" then -- it is another tag
                    if tags[content[i].tag] ~= nil then
                        tags[content[i].tag](content[i].arg)
                    elseif tags.closing[content[i].tag] ~= nil then
                        tags.closing[content[i].tag](content[i].arg, content[i].contents)
                    end
                end
            end
            term.setTextColor(oldColor)
            --table.remove(buffer.color,#buffer.color) -- removing the currently used color from the buffer
        end,
        ["HL"] = function(arg,content) -- Maybe Word wrapping should go here? see [P] k
            local ccolor = term.getBackgroundColor()
            term.setBackgroundColor(colors.arg)
            term.write(content)
            term.setBackgroundColor(ccolor)
        end,
        ["TITLE"] = function(arg,content) 
            --append the content to the page title
            --local pTitle = --todo --this should be declared in the gui
        end,
        ["TAB"] = function(arg,content)
            --display this in the tab text, but not the title bar
        end,
        ["NOTAB"] = function(arg,content)
            --display this in the title bar, but not the tab text
        end,
        ["P"] = function(args,content)
            for i=1, #content do
                if type(content[i]) == "string" then
                    wrap(content[i],args)
                elseif type(content[i]) == "table" then -- it is another tag
                    if tags[content[i].tag] ~= nil then
                        tags[content[i].tag](content[i].arg)
                    elseif tags.closing[content[i]] ~= nil then
                        tags.closing[content[i].tag](content[i].arg, content[i].contents)
                    end
                end
            end
        end,
        ["CELL"] = function(args,content)
            --makes a [P] but wrap X chars from where the tag started
            --int between 1 and 50 (50 being the width of the window)
            local xArg,yArg = args:match("(%d+),(%d+)")
            if tonumber(x) == nil or tonumber(y) == nil then
                return
            end
            local x,y = term.getCursorPos()
            local cellWindow = window.create(term.current(),x,y,xArg,yArg)
            local oldCur = term.current()
            term.redirect(cellWindow)
            for i=1, #content do
                if type(content[i]) == "string" then
                    wrap(content[i],xArg)
                elseif type(content[i]) == "table" then -- it is another tag
                    if tags[content[i].tag] ~= nil then
                        tags[content[i].tag](content[i].arg)
                    elseif tags.closing[content[i]] ~= nil then
                        tags.closing[content[i].tag](content[i].arg, content[i].contents)
                    end
                end
            end
            term.redirect(term.current)
        end,
        ["CENTER"] = function(args,content)
            local pos = {term.getCursorPos()}
            if pos[1] ~= 1 then
                term.setCursorPos(1,pos[2]+1)
            end
            for i=1, #content do
                if type(content[i]) == "string" then
                    local x,y = term.getSize()
                    local cy = pos[2]
                    term.setCursorPos(x/2-(#content[#content]/2),cy)
                    print(content[i])
                elseif type(content[i]) == "table" then -- it is another tag
                    if tags[content[i].tag] ~= nil then
                        tags[content[i].tag](content[i].arg)
                    elseif tags.closing[content[i]] ~= nil then
                        tags.closing[content[i].tag](content[i].arg, content[i].contents)
                    end
                end
            end
        end,
        ["A"] = function(args,content)
            --link. argument is .kst URL
            -- need to register a button by getting starting 2 positions, and ending 2 
            for i = 1, #content do
                if type(content[i]) == "string" then
                    term.write(content[i],args)
                elseif type(content[i]) == "table" then -- it is another tag
                    if tags[content[i].tag] ~= nil then
                        tags[content[i].tag](content[i].arg)
                    elseif tags.closing[content[i]] ~= nil then
                        tags.closing[content[i].tag](content[i].arg, content[i].contents)
                    end
                end
            end
        end,
        ["D"] = function(args,content)
            --downloads file at URL to computer, otherwise same as [A]
        end,
        ["NFP"] = function(args,content)
            --don't use the paintutils api either - we need to measure its length
            --draw a paintutils image
        end,
        ["NFT"] = function(args,content)
            --draw a nitropaint image
        end,
        ["SKCH"] = function(arg,content)
            --draw an image in oeed's format
            --by no means important for release
        end,
        ["SCRIPT"] = function(arg,content)
           -- sandboxed lua, but our own functions that we parse
           --by no means important for release
        end,
        ["LIST"] = function(arg,content)
           -- going to have to put special stuff in parser for this
           -- alternatively we could just make one for list items
           -- this could handle things nested in it differently, which would work
           --by no means important for release
        end,
        ["H"] = function(arg,content)
           -- write in block letters (we can load NFP images for this)
           --by no means important for release
        end,
        ["SET"] = function(arg,content)
           --sets cookie "arg" equal to "content"
           --by no means important for release
        end
    },
    ["EAT"] = function(arg)
        --deletes cookie "arg"
    end,
    ["FEAST"] = function()
        --deletes all cookies set by the site
    end,
    ["BG"] = function(arg)
        term.setBackgroundColor(color[arg])
    end,
    ["BR"] = function()
        --linebreak
        local x,y = term.getCursorPos()
        term.setCursorPos(1,y+1)
    end,
    ["LN"] = function()
        --linebreak, but only if the cursor is not at the beginning
        local x,y = term.getCursorPos()
        if x ~= 1 then
            term.setCursorPos(1,y+1)
        end
    end,
    ["CR"] = function()
        --carraige return - move cursor x to the first spot in the container
        local x,y = term.getCursorPos()
        term.setCursorPos(1,y)
    end,
    ["LF"] = function()
        --move cursor down one (linefeed)
        local x,y = term.getCursorPos()
        term.setCursorPos(x,y+1)
    end,
    ["UP"] = function()
        --move cursor up one
        local x,y = term.getCursorPos()
        term.setCursorPos(x,y-1)
    end,
    ["SKIP"] = function(args)
        --move right X places
        local pos = {term.getCursorPos()}
        arg = tonumber(args) or 0
        term.setCursorPos(pos[1]+arg,pos[2])
    end,
    ["BS"] = function(args)
        --move left X places
        local pos = {term.getCursorPos()}
        arg = tonumber(args) or 0
        term.setCursorPos(pos[1]+arg,pos[2])
    end,
    ["TOP"] = function(args)
        --move cursor to top left corner of page or cell
        term.setCursorPos(1,1)
    end,
    ["CLEAR"] = function(args)
        --disregard everything before the last instance of this tag - actually don't, because containers
        term.clear()
    end,
    ["END"] = function(args)
        --disregard everything after the first instance of this tag
        --this function technically should never be reached
        return "doneloading"
    end,
    ["NSFW"] = function(args)
        --throw an error if the NSFW filter is on
        --this variable should be defined in the gui, not here
        local nsfw = true
    end,
    ["HR"] = function(args)
        --draw a horizontal bar
        --a bunch of gray dashes that are as wide as the container
        local pos = {term.getCursorPos()}
        if pos[1] ~= 1 then
            term.setCursorPos(1,pos[2]+1)
        end
        local ccolor = {term.getTextColor()}
        term.setTextColor(args)
        for i = 1,(pos[1]-1)-pos[1] do
            term.write("-")
        end
        term.setTextColor(ccolor)
    end,
    ["REFRESH"] = function(arg)
        --refresh the page in X seconds. no faster than 2? seconds
    end,
    ["REDIRECT"] = function(arg)
        --same as [A] but go directly to the specified page
    end,
    --afterwards, in order by importance - lists, scripts, SKCH/NFA images, big letters, forms, tables
}
setmetatable(tags,{
    __call = function(self,tag,arg)
        arg = ":"..arg or ""
        if self[tag] ~= nil or self.closing[tag]  ~= nil then
            return true
        else
            return "["..tag..arg.."]"
        end
    end})
local function prerequisition(siteContents)
    -- [END]
    -- This might leave a [ on the page. I can't remember if we fixed that
    siteContents = siteContents:sub(1,siteContents:find("%[END%]")-1)
    -- [ID]
    siteContents = siteContents:gsub("%[ID%]",os.getComputerID())
    -- [HOSTNAME] - get the server's name from the DNS
    -- [CHECK:arg] - replace with contents of cookie "arg"
    -- [CLOSE] - just don't parse, close the tab
end
local function parser(siteContents)
    siteContents = prerequisition(siteContents)
    local siteData = {}
    local curPath = ""
        
    local function getfield (f)
        local v = siteData    -- start with the table of globals
        if f ~= "" then
            for w in string.gmatch(f, "[%w_]+") do
                w = tonumber(w) or w
                v = v[w]
            end
        end
        if type(v) == "nil" then
          print(f)
          print(textutils.serialize(siteData))
        end
        return v
    end

    local function setfield (f,v)
        local t = siteData           -- start with the table of globals
            for w, d in string.gmatch(f, "([%w_]+)(.?)") do
              w = tonumber(w) or w
                if d == "." then      -- not last field?
                    t[w] = t[w] or {}   -- create table if absent
                    t = t[w]            -- get the table
                else         
                if type(t[#t]) == "string" and type(v) == "string" then
                    t[#t] = t[#t] .. v
                else
                    t[w] = v            -- do the assignment
                end
            end
        end
    end
    
    local function processBlock(str)
        local content = {}
        repeat
            local bx,ex = str:find("%[[^ %s%]]+%]")
            if bx==nil then
                table.insert(content,str)
                str = ""
            else
                local tag = str:sub(bx,ex)
                local argLoc = tag:find(":") or #tag
                local t = tag:sub(2,argLoc-1)
                local a = tag:sub(argLoc+1, #tag-1)
                if type(tags.closing[t]) ~= "nil" then
                    table.insert(content,str:sub(1,bx-1))
                    table.insert(content,{tag = t,arg = a,contents = {}})
                    str = str:sub(ex+1,#str)
                elseif type(tags[t]) ~= "nil" then
                    table.insert(content,str:sub(1,bx-1))
                    table.insert(content,{tag = t, arg = a})
                    str = str:sub(ex+1, #str)
                elseif type(tags.closing[t:sub(2,#t)]) ~= "nil" then
                    table.insert(content,str:sub(1,bx-1))
                    table.insert(content,{tag = t})
                    str = str:sub(ex+1, #str)
                else
                    if type(content[#content]) =="string" then
                        content[#content] = content[#content]..str:sub(1,ex)
                        str = str:sub(ex+1,#str)
                    else
                        table.insert(content,str:sub(1,ex))
                        str = str:sub(ex+1,#str)
                    end
                end
            end
        until #str == 0
        return content
    end    
    
    local function tokenise(...)
        local sLine = table.concat({...}," ")
        local tWords = {}
        local bQuoted = false
        for match in string.gmatch(sLine .. "\"", "(.-)\"") do
            if bquoted == false then
                table.insert( tWords, match )
            else
                for m in string.gmatch( match, "[^ \t]+%s*" ) do
                    table.insert( tWords, m )
                end
            end
        end
        return tWords
    end
    
    for _,block in pairs(tokenise(siteContents)) do
        local contents = processBlock(block)
        for i = 1, #contents do
            local curFile = getfield(curPath)
            if type(contents[i]) == "string" then -- its a word not a tag
                if type(curFile[#curFile]) == "string" then
                    setfield( curPath.."."..#curFile , contents[i] )
                elseif type(curFile[#curFile]) == "table" then
                    setfield( curPath.."."..(#curFile+1) , contents[i] )
                elseif #curFile == 0 then
                    setfield( curPath.."."..1 , contents[i] )
                end
                
            elseif type(contents[i]) == "table" then -- its a tag
                if contents[i].tag:sub(1,1)=="/" then
                    local str = curPath:sub(1,-10)
                    curPath = str:sub(1,-(#str:gsub("%w+%.",""))+2)
                elseif type(tags[contents[i].tag]) ~= "nil" then
                    setfield( curPath.."."..(#curFile+1) , contents[i])
                elseif type(tags.closing[contents[i].tag]) ~= "nil" then
                    curPath = curPath.."."..#curFile+1
                    setfield( curPath , contents[i] )
                    curPath = curPath..".".."contents"
                end
            else
                error("Invalid data type during parsing: " .. type(contents[i]))
            end
        end
    end
    return siteData
end

local function assemble(tbl)
    for i = 1, #tbl do
        if type(tbl[i]) == "string" then
            term.write(tbl[i])
        elseif type(tbl[i]) == "table" then
            if tags[tbl[i].tag] then
                tags[tbl[i].tag](tbl[i].arg)
            elseif tags.closing[tbl[i].tag] then
                tags.closing[tbl[i].tag](tbl[i].arg,tbl[i].contents)
            end
        end
    end
end
-- END kst/parsers/ksml/coal
-- BEGIN kristscape

local headers = {}
local function readurl(url)
    local addressbar = url
    if addressbar:find("://") == nil then
        addressbar = "http://" .. addressbar
    end
    addressbar = addressbar:gsub(".com",".kst")
    addressbar = addressbar:gsub(".org",".kst")
    addressbar = addressbar:gsub(".net",".kst")
    
    local protocol = string.sub(addressbar,1,string.find(addressbar,"://")-1)
    local name = ""
    if protocol == "http" then
        name = string.sub(url,8,string.find(url,".kst")-1)
        name = http.get("http://krist.ceriat.net/?a="..name,nil,headers).readAll()
    elseif protocol == "rdnt"
        
    elseif protocol == "knet"
        
    elseif protocol == "file"
        
    elseif protocol == "scape"
        name = string.sub(url,string.find(url,"://")+3,string.find(url,"/"-1))
    end
    url = url:sub(url:find("://"))
    local destination = url:sub(url:find("/")+1)
    
    
    return addressbar, protocol, destination
end
--END kristscape




















--[[To Do list
1: Finish Tags
2: Refresh
3: Redirect
4: Cells
5: Bugfixes
6: Donuts
]]--
--[[Tags no longer in use
    ["COLORLINKS"] = function(arg)
        --true or false - if true (default) [A] will draw in cyan text
    end,
    Reason no longer in use:actually don't do this one. we can just put a [C] tag in the link's text, and have [A] add cyan to the color table
]]--
--[[ New parser example

{    --start of the site Contents
    { -- opening of a new tag
        tag = "C",
        arg = "arg",
        contents = { -- what is between the opening and closing tag
             "Hello my name is text",
             { -- opening of a tag
                 tag = "BG",
                 arg = "RED",
                 contents = { -- only closing tags will have this
                      "More Txt"   
                 }    
             },
             "Some more text"   
        }    --closing of a tag
    }   --closing of the tag
}
    
]]--