// Solutions to the Advent Of Code 2024.
// Copyright (C) 2025  Stefan Müller
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// this program.  If not, see <http://www.gnu.org/licenses/>.

#include <aoc/WarehouseWoes.hpp>

#include <sstream>

WarehouseWoes::WarehouseWoes(const int inputFileNameSuffix)
    : LinesSolver{ inputFileNameSuffix }, isProcessingMap_{ true }, isSearchingStartPosition_{ true }, lines2_{},
      warehouseBoxPusher1_{ getBoxChar(), getWallChar(), getEmptyChar() },
      warehouseBoxPusher2_{ getLeftWideBoxChar(), getRightWideBoxChar(), getWallChar(), getEmptyChar() }
{
}

const std::string WarehouseWoes::getPuzzleName() const
{
    return "Warehouse Woes";
}

const int WarehouseWoes::getPuzzleDay() const
{
    return 15;
}

void WarehouseWoes::processDataLine(const std::string& line)
{
    if (!line.empty())
    {
        if (isProcessingMap_)
        {
            checkStartingPosition(line);
            LinesSolver::processDataLine(line);
            addWarehouseMap2Line(line);
        }
        else
        {
            warehouseBoxPusher1_.processMovements(lines, line);
            warehouseBoxPusher2_.processMovements(lines2_, line);
        }
    }
    else
    {
        isProcessingMap_ = false;
    }
}

void WarehouseWoes::finish()
{
    part1 = calcAllGpsCoordinates(lines, getBoxChar());
    part2 = calcAllGpsCoordinates(lines2_, getLeftWideBoxChar());
}

constexpr char WarehouseWoes::getRobotChar()
{
    return '@';
}

constexpr char WarehouseWoes::getBoxChar()
{
    return 'O';
}

constexpr char WarehouseWoes::getLeftWideBoxChar()
{
    return '[';
}

constexpr char WarehouseWoes::getRightWideBoxChar()
{
    return ']';
}

constexpr char WarehouseWoes::getWallChar()
{
    return '#';
}

constexpr char WarehouseWoes::getEmptyChar()
{
    return '.';
}

void WarehouseWoes::checkStartingPosition(const std::string& line)
{
    if (isSearchingStartPosition_)
    {
        auto hit = line.find(getRobotChar());
        if (hit != std::string::npos)
        {
            int x = static_cast<int>(hit);
            int y = static_cast<int>(lines.size());
            warehouseBoxPusher1_.setRobotPosition({ x, y });
            warehouseBoxPusher2_.setRobotPosition({ x * 2, y });
            isSearchingStartPosition_ = false;
        }
    }
}

void WarehouseWoes::addWarehouseMap2Line(const std::string& line)
{
    std::ostringstream stream{};
    for (const char c : line)
    {
        if (c == getBoxChar())
        {
            stream << getLeftWideBoxChar() << getRightWideBoxChar();
        }
        else
        {
            stream << c << c;
        }
    }
    lines2_.push_back(stream.str());
}

long long int WarehouseWoes::calcAllGpsCoordinates(Lines& warehouseMap, const char boxChar)
{
    long long int result{ 0 };
    for (size_t j = 0; j < warehouseMap.size(); j++)
    {
        for (size_t i = 0; i < warehouseMap[j].size(); i++)
        {
            if (warehouseMap[j][i] == boxChar)
            {
                result += calcGpsCoordinate(i, j);
            }
        }
    }
    return result;
}

long long int WarehouseWoes::calcGpsCoordinate(const size_t x, const size_t y)
{
    return x + 100 * y;
}