﻿#include <Windows.h>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>

#include "DxLib.h"

#include "Stage.h"

namespace
{
    // 負の座標も正しく扱えるよう、0方向ではなく下方向へ丸める
    int PixelToTile(int pixel)
    {
        if (pixel >= 0) return pixel / Stage::tileSize;
        return (pixel - Stage::tileSize + 1) / Stage::tileSize;
    }
}

bool Stage::Initialize(const wchar_t* csvPath)
{
    loadedFromCsv = LoadFromCsv(csvPath);
    if (!loadedFromCsv)
    {
        const wchar_t* failedPath = (csvPath != nullptr) ? csvPath : L"(null)";
        std::wstring message = std::wstring(failedPath) + L" を読み込めません。";
        MessageBoxW(nullptr, message.c_str(),
            L"Map Load Error", MB_OK);
        return false;
    }

    return true;
}

bool Stage::LoadFromCsv(const wchar_t* csvPath)
{
    // filesystem::pathを介して、日本語を含むWindowsパスを開く
    std::ifstream file{ std::filesystem::path(csvPath) };
    if (!file.is_open())
    {
        return false;
    }

    ClearMap();

    bool foundGoal = false;
    std::string line;
    int y = 0;

    while (y < mapHeight && std::getline(file, line))
    {
        std::stringstream lineStream(line);
        std::string cell;
        int x = 0;

        while (x < mapWidth && std::getline(lineStream, cell, ','))
        {
            int value = 0;

            try
            {
                value = std::stoi(cell);
            }
            catch (...)
            {
                // 数値でないセルは空白タイルとして扱う
                value = 0;
            }

            if (value == 1)
            {
                tiles[y][x] = 1;
            }
            else if (value == 2)
            {
                tiles[y][x] = 0;
                if (!foundGoal)
                {
                    SetGoalTile(x, y);
                    foundGoal = true;
                }
            }
            else if (value == 3)
            {
                tiles[y][x] = 0;
                AddEnemySpawn(x, y);
            }

            x++;
        }

        y++;
    }

    return foundGoal;
}

bool Stage::IsSolidTile(int tileX, int tileY) const
{
    if (tileX < 0 || tileX >= mapWidth) return true;
    if (tileY < 0) return true;
    if (tileY >= mapHeight) return false;

    return tiles[tileY][tileX] == 1;
}

bool Stage::IsSolidAtRect(const Rect& rect) const
{
    const int leftTile = PixelToTile(rect.x);
    const int rightTile = PixelToTile(rect.x + rect.width - 1);
    const int topTile = PixelToTile(rect.y);
    const int bottomTile = PixelToTile(rect.y + rect.height - 1);

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (IsSolidTile(x, y)) return true;
        }
    }

    return false;
}

bool Stage::ResolveHorizontal(Rect& rect, int moveX) const
{
    if (moveX == 0) return false;

    rect.x += moveX;
    const int leftTile = PixelToTile(rect.x);
    const int rightTile = PixelToTile(rect.x + rect.width - 1);
    const int topTile = PixelToTile(rect.y);
    const int bottomTile = PixelToTile(rect.y + rect.height - 1);
    bool hit = false;
    int resolvedX = rect.x;

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (!IsSolidTile(x, y)) continue;

            const int candidate = (moveX > 0)
                ? x * tileSize - rect.width
                : (x + 1) * tileSize;
            if (!hit ||
                (moveX > 0 && candidate < resolvedX) ||
                (moveX < 0 && candidate > resolvedX))
            {
                resolvedX = candidate;
            }
            hit = true;
        }
    }

    rect.x = resolvedX;
    return hit;
}

bool Stage::ResolveVertical(Rect& rect, int moveY) const
{
    if (moveY == 0) return false;

    rect.y += moveY;
    const int leftTile = PixelToTile(rect.x);
    const int rightTile = PixelToTile(rect.x + rect.width - 1);
    const int topTile = PixelToTile(rect.y);
    const int bottomTile = PixelToTile(rect.y + rect.height - 1);
    bool hit = false;
    int resolvedY = rect.y;

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (!IsSolidTile(x, y)) continue;

            const int candidate = (moveY > 0)
                ? y * tileSize - rect.height
                : (y + 1) * tileSize;
            if (!hit ||
                (moveY > 0 && candidate < resolvedY) ||
                (moveY < 0 && candidate > resolvedY))
            {
                resolvedY = candidate;
            }
            hit = true;
        }
    }

    rect.y = resolvedY;
    return hit;
}
void Stage::Draw(int cameraX, const ImageManager& images) const
{
    // カメラ内に見える列だけを描画する
    const int firstTileX = cameraX / tileSize;
    const int lastTileX = (cameraX + 640) / tileSize + 1;

    for (int y = 0; y < mapHeight; y++)
    {
        for (int x = firstTileX; x <= lastTileX; x++)
        {
            if (x < 0 || x >= mapWidth) continue;
            if (tiles[y][x] == 0) continue;

            DrawTile(x * tileSize - cameraX, y * tileSize, images);
        }
    }

    DrawGoal(cameraX, images);
}

Rect Stage::GetGoal() const
{
    return goal;
}

int Stage::GetEnemySpawnCount() const
{
    return enemySpawnCount;
}

SpawnPoint Stage::GetEnemySpawn(int index) const
{
    if (index < 0 || index >= enemySpawnCount) return { 0, 0 };
    return enemySpawns[index];
}

bool Stage::IsLoadedFromCsv() const
{
    return loadedFromCsv;
}

void Stage::ClearMap()
{
    // 前回の読み込み結果を残さない
    for (int y = 0; y < mapHeight; y++)
    {
        for (int x = 0; x < mapWidth; x++)
        {
            tiles[y][x] = 0;
        }
    }

    goal = { (mapWidth - 4) * tileSize, (mapHeight - 3) * tileSize,
        tileSize, tileSize };
    enemySpawnCount = 0;
}

void Stage::SetGoalTile(int tileX, int tileY)
{
    goal.x = tileX * tileSize;
    goal.y = tileY * tileSize;
    goal.width = tileSize;
    goal.height = tileSize;
}

void Stage::AddEnemySpawn(int tileX, int tileY)
{
    if (enemySpawnCount >= maxEnemySpawns) return;

    enemySpawns[enemySpawnCount] = {
        tileX * tileSize,
        tileY * tileSize
    };
    enemySpawnCount++;
}

void Stage::DrawTile(int screenX, int screenY, const ImageManager& images) const
{
    DrawExtendGraph(
        screenX, screenY,
        screenX + tileSize, screenY + tileSize,
        images.Get(ImageId::Tile).handle, TRUE);
}

void Stage::DrawGoal(int cameraX, const ImageManager& images) const
{
    const int screenX = goal.x - cameraX;
    const int screenY = goal.y;

    DrawExtendGraph(
        screenX, screenY,
        screenX + goal.width, screenY + goal.height,
        images.Get(ImageId::Goal).handle, TRUE);
}

