--[[
Copyright © 2017, Sammeh of Quetzalcoatl
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of JobChange nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Sammeh BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]

_addon.name = 'Job Change'
_addon.author = 'Sammeh; Akaden'
_addon.version = '1.0.4'
_addon.command = 'jc'

-- 1.0.1 first release
-- 1.0.2 added 'reset' command to simply reset to existing job.  Changes sub job to a random starting job and back.
-- 1.0.3 Code clean-up
-- 1.0.4 Added /jc main/sub command and organized solve for fewest changes.

require('tables')
packets = require('packets')
res = require ('resources')

local temp_jobs =  T { 'NIN', 'DNC', 'WAR', 'MNK', 'WHM', 'BLM', 'RDM', 'THF' } 
local mog_zones = S { 'Selbina', 'Mhaura', 'Tavnazian Safehold', 'Nashmau', 'Rabao', 'Kazham', 'Norg', 'Walk of Echoes [P1]', 'Walk of Echoes [P2]' }
local moogles = S { 'Moogle', 'Nomad Moogle', 'Green Thumb Moogle', 'Pilgrim Moogle' }

local log = function(msg)
    windower.add_to_chat(4,'JobChange: '..msg)
end

local jobchange = function(job, main)
    local packet = packets.new('outgoing', 0x100, {
        [(main and 'Main' or 'Sub')..' Job'] = job,
    })
    packets.inject(packet)
end

local find_conflict = function(job_name, p)
    if p.main_job == job_name:upper() then
        return 'main'
    end
    if p.sub_job == job_name:upper() then
        return 'sub'
    end
end

local find_temp_job = function(p)
    for _, job_name in ipairs(temp_jobs) do
        if not find_conflict(job_name, p) and p.jobs[job_name:upper()] > 0 then 
            for index,value in pairs(res.jobs) do
                if value.ens == job_name then
                    return index
                end
            end
        end
    end
end

local find_job = function(job,p)
    if job == nil then return nil end
    local jobLevel = p.jobs[job:upper()]
    for index,value in pairs(res.jobs) do
        if value.ens:lower() == job and jobLevel > 0 then 
            return index
        end
    end
end

local find_job_change_npc = function()
    local info = windower.ffxi.get_info()
    if not (info.mog_house or mog_zones:contains(res.zones[info.zone].english)) then
        log('Not in a zone with a Change NPC')
        return
    end

    for _, v in pairs(windower.ffxi.get_mob_array()) do
        if v.distance < 36 and v.valid_target and moogles:contains(v.name) then
            return v
        end
    end
end

windower.register_event('addon command', function(command, ...)
    local p = windower.ffxi.get_player()
    local args = L{...}
    local job = ''
    if args[1] then 
        job = args[1]:lower()
    end
    local main = nil
    local sub = nil
    if command:lower() == 'main' then
        main = job
        if main and main:upper() == p.main_job then main = nil end
    elseif command:lower() == 'sub' then
        sub = job
        if sub and sub:upper() == p.sub_job then main = nil end
    elseif command:lower() == 'reset' then
        log('Resetting Job')
        sub = p.sub_job:lower()
    elseif command:contains('/') or command:contains('\\') then
        command = command:gsub('\\','/')
        local js = command:split('/')
        main = (js[1] ~= '' and js[1] or nil)
        sub = (js[2] ~= '' and js[2] or nil)
        -- remove identicals.
        if main and main:upper() == p.main_job then main = nil end
        if sub and sub:upper() == p.sub_job then sub = nil end
    elseif command ~= nil and command ~= '' then
        main = command:lower()
        if main and main:upper() == p.main_job then main = nil end
    else
        log('Syntax: //jc main|sub JOB  -- Chnages main or sub to target JOB')
        log('Syntax: //jc main/sub  -- Changes main and sub')
        log('Syntax: //jc reset -- Resets Current Job')
        return
    end

    local changes = T{}

    local main_id = find_job(main, p)
    if main ~= nil and main_id == nil then 
        log('Could not change main job to to '..main:upper()..' ---Mistype|NotUnlocked')
        return
    end
    local sub_id = find_job(sub, p)
    if sub ~= nil and sub_id == nil then 
        log('Could not change sub job to to '..sub:upper()..' ---Mistype|NotUnlocked')
        return
    end

    if main_id == nil and sub_id == nil then
        log('No change required.')
        return
    end

    if main_id ~= nil and main:upper() == p.sub_job then
        if sub_id ~= nil and sub:upper() == p.main_job then
            changes:append({job_id=find_temp_job(p), is_conflict=true, is_main=false})
            changes:append({job_id=main_id, is_main=true})
            changes:append({job_id=sub_id, is_main=false})
        else
            if sub_id ~= nil then
                changes:append({job_id=sub_id, is_main=false})
            else
                changes:append({job_id=find_temp_job(p), is_conflict=true, is_main=false})
            end
            changes:append({job_id=main_id, is_main=true})
        end
    elseif sub_id ~= nil and sub:upper() == p.main_job then
        if main_id ~= nil then
            changes:append({job_id=main_id, is_main=true})
        else
            changes:append({job_id=find_temp_job(p), is_conflict=true, is_main=true})
        end
        changes:append({job_id=sub_id, is_main=false})
    else
        if main_id ~= nil then
            if main:upper() == p.main_job then
                changes:append({job_id=find_temp_job(p), is_conflict=true, is_main=true})
            end
            changes:append({job_id=main_id, is_main=true})
        end
        if sub_id ~= nil then
            if sub:upper() == p.sub_job then
                changes:append({job_id=find_temp_job(p), is_conflict=true, is_main=false})
            end
            changes:append({job_id=sub_id, is_main=false})
        end
    end

    local npc = find_job_change_npc()
    if npc then
        for _, change in ipairs(changes) do
            if change.is_conflict then
                log('Conflict with '..(change.is_main and 'main' or 'sub')..' job. Changing to: '..res.jobs[change.job_id].ens)
            else
                log('Changing '..(change.is_main and 'main' or 'sub')..' job to: '..res.jobs[change.job_id].ens)
            end
            jobchange(change.job_id, change.is_main)

            coroutine.sleep(0.5)
        end
    else
        log('Not close enough to a Moogle!')
    end       
end)
