require("Turtle")
Wheat = Turtle:new()

function Wheat:getStatusDown()
    local bd, data = turtle.inspectDown()
    if(bd == true) then
        if(data.state.age == nil) then
            return -1
        end
        return data.state.age
    end
    if(turtle.digDown() == true) then
        return -2
    end
    if(self:selectPlantable(true)) then
        if(turtle.placeDown() == true) then
            turtle.digDown()
            return -2
        end
        return -1
    end
    return false
end

function Wheat:isPlantable(slot, update)
    self:assertInvUpdate(update)
    if(type(self.inv[slot]) ~= "table") then
        return false
    end
    if(type(self.inv[slot].tags) ~= "table") then
        return false
    end
    return self.inv[slot].tags["forge:seeds"] == true
end

function Wheat:getPlantable(update)
    self:assertInvUpdate(update)
    for i = 1, 16 do
        if(self:isPlantable(i, false)) then
            return i
        end
    end
    return false
end

function Wheat:selectPlantable(update)
    self:assertInvUpdate(update)
    local slot = self:getPlantable(false)
    if(slot == false) then
        return false
    else
        turtle.select(slot)
        return true
    end
end

function Wheat:readConfig(path)
    -- reading configFile into a table
    if (path == nil) then
        term.write("init must have a path for the config file as argument")
        return -1
    end
    if (type(path) ~= "string") then
        term.write("invalid argument")
        return -1
    end
    if (not fs.exists(path)) then
        term.write("could not find config file \n please make sure it exists and the path is correct")
        return -1
    end
    local file = fs.open(path, "r")
    local config = {}
    while true do
        local line = file.readLine()
        if not line then
            break
        end
        local numLine = tonumber(line)
        config[#config+1] = numLine
    end
    file.close()

    self.homePos = {config[1], config[2], config[3], config[4]}
    self.length = config[5]
    self.width = config[6]
end

function Wheat:resolveTarget(target)
    if target == "home" then
        target = self.homePos
    elseif target == "seedChest" then
        target = self.homePos
        target = self:offset(target, target[4] - 1, 1)
    elseif target == "coalChest" then
        target = self.homePos
        target = self:offset(target, target[4] -1, 2)
    elseif target == "outputChest" then
        target = self.homePos
    elseif target == "firstCrop" then
        target = self.homePos
        target = self:offset(target, target[4], 1)
    end

    self:assertPositionFormat(target)
    self:log("target resolved")
    return target
end

function Wheat:createMatrix(goBack)
    if (goBack == nil) then goBack = true end
    if(goBack == true) then
        goBack = {self.x, self.y, self.z, self.dir}
    end
    self:moveTo("firstCrop")
    self.cropMatrix = {}
    for i = 1, self.width do
        local line = {}
        for j = 1, self.length do
            line[j] = self:getStatusDown()
            if(j < self.length) then
                digForward()
            end
        end
        if(i % 2 == 0) then
            self.cropMatrix[i] = self:reverseArray(line)
        else
            self.cropMatrix[i] = line
        end
        if(i < self.width) then
            if(i % 2 == 0) then
                turtle.turnRight()
                digForward()
                turtle.turnRight()
                self.dir = (self.dir + 2) % 4
            else
                turtle.turnLeft()
                digForward()
                turtle.turnLeft()
                self.dir = (self.dir + 2) % 4
            end
        end
    end
    self.x, self.y, self.z = gps.locate()
    if (goBack ~= false) then
        self:log("moving to goBack")
        self:moveTo(goBack)
    end
end

function Wheat:clearInv(goBack)
    if (goBack == nil) then goBack = true end
    if(goBack == true) then
        goBack = {self.x, self.y, self.z, self.dir}
    end
    if (self:isInInv("seed", true)) then
        self:moveTo("seedChest")
        self:dropItemsDown("seed", false)
    end
    if (self:isInInv("coal", false)) then
        self:moveTo("coalChest")
        self:dropItemsDown("coal", false)
    end
    if (self:isInvEmpty(true) == false) then
        self:moveTo("outputChest")
        self:dropItemsDown("any", false)
    end

    if (goBack ~= false) then
        self:log("moving to goBack")
        self:moveTo(goBack)
    end
end

function Wheat:fillSeeds(goBack)
    if (goBack == nil) then goBack = true end
    if(goBack == true) then
        goBack = {self.x, self.y, self.z, self.dir}
    end

    self:clearInv("seedChest")
    local seedsRemaining = self:calculateSeeds()
    for i = 1, 16 do
        turtle.select(i)
        local amount
        if(seedsRemaining > 64) then
            amount = 64
        else
            amount = seedsRemaining
        end
        turtle.suckDown(amount)
        if(turtle.getItemCount() ~= amount) then
            return false
        end
        seedsRemaining = seedsRemaining - amount
        if(seedsRemaining == 0) then
            break
        end
    end
    if (goBack ~= false) then
        self:log("moving to goBack")
        self:moveTo(goBack)
    end
end

function Wheat:calculateSeeds()
    local seeds = 0
    for i = 1, #self.cropMatrix do
        local line = self.cropMatrix[i]
        for j = 1, #line do
            if line[j] == -2 then
                seeds = seeds + 1
            end
        end
    end
    return seeds
end

function Wheat:plantAll(goBack)
    if (goBack == nil) then goBack = true end
    if(goBack == true) then
        goBack = {self.x, self.y, self.z, self.dir}
    end

    self:fillSeeds("firstCrop")
    for i = 1, self.width do
        local line = self.cropMatrix[i]
        if(i % 2 == 0) then
            line = self:reverseArray(line)
        end
        for j = 1, self.length do
            if(line[j] == -2) then
                self:selectPlantable(true)
                turtle.placeDown()
                line[j] = 0
            elseif(line[j] >= 0) then
                line[j] = self:getStatusDown()
            end
            if(j < self.length) then
                digForward()
            end
        end
        if(i % 2 == 0) then
            self.cropMatrix[i] = self:reverseArray(line)
        else
            self.cropMatrix[i] = line
        end
        if(i < self.width) then
            if(i % 2 == 0) then
                turtle.turnRight()
                digForward()
                turtle.turnRight()
                self.dir = (self.dir + 2) % 4
            else
                turtle.turnLeft()
                digForward()
                turtle.turnLeft()
                self.dir = (self.dir + 2) % 4
            end
        end
    end
    self.x, self.y, self.z = gps.locate()
    if (goBack ~= false) then
        self:log("moving to goBack")
        self:moveTo(goBack)
    end
end

function Wheat:harvestAll(goBack)
    if (goBack == nil) then goBack = true end
    if(goBack == true) then
        goBack = {self.x, self.y, self.z, self.dir}
    end
    self:clearInv("seedChest")
    turtle.select(1)
    turtle.suckDown()
    self:moveTo("firstCrop")
    for i = 1, self.width do
        local line = self.cropMatrix[i]
        if(i % 2 == 0) then
            line = self:reverseArray(line)
        end
        for j = 1, self.length do
            if(line[j] >= 0) then
                line[j] = self:getStatusDown()
            end
            if(line[j] == 7) then
                turtle.digDown()
                turtle.placeDown()
            end
            if(j < self.length) then
                digForward()
            end
        end
        if(i % 2 == 0) then
            self.cropMatrix[i] = self:reverseArray(line)
        else
            self.cropMatrix[i] = line
        end
        if(i < self.width) then
            if(i % 2 == 0) then
                turtle.turnRight()
                digForward()
                turtle.turnRight()
                self.dir = (self.dir + 2) % 4
            else
                turtle.turnLeft()
                digForward()
                turtle.turnLeft()
                self.dir = (self.dir + 2) % 4
            end
        end
    end
    self.x, self.y, self.z = gps.locate()
    if (goBack ~= false) then
        self:log("moving to goBack")
        self:moveTo(goBack)
    end

end

function Wheat:getMatrixString()
    local s = self:arrayToString(self.cropMatrix)
    return s
end

function Wheat:arrayToString(a)
    local s = "{"
    for i, v in ipairs(a) do
        if(type(v) == "table") then
            s = s .. self:arrayToString(v)
        else
            s = s .. tostring(v)
        end
        if(i < #a) then
            s = s .. ","
        end
    end
    s = s .. "}"
    return s
end