#include "crSpriteAtlas.h"

#include <SDL3/SDL.h>

#include "crText.h"
#include "crTextureLoader.h"

bool crSpriteAtlas::Init( const char* baseName )
{
    char path[ 256 ];

    SDL_snprintf( path, sizeof( path ), "crassets/texture/%s.ktx2", baseName );
    const crTexture loaded = crTextureLoader::Load( path );
    if( loaded.handle == 0 )
        return false;

    _texture     = loaded.handle;
    _textureSize = loaded.size;

    SDL_snprintf( path, sizeof( path ), "crassets/texture/%s.cratlas", baseName );
    if( ParseAtlasCsv( path ) == false )
    {
        Cleanup();
        return false;
    }

    SDL_LogTrace( CR_LOG_CATEGORY_ASSET_TEXTURE, "crSpriteAtlas: loaded( %s, %dx%d, sprites:%d )", baseName, _textureSize.x, _textureSize.y, _sprites.Size() );
    return true;
}

void crSpriteAtlas::Cleanup()
{
    glDeleteTextures( 1, &_texture );
    _texture = 0;

    _sprites.Clear();
}

int32_t crSpriteAtlas::Find( const char* name ) const
{
    int32_t sz = _sprites.Size();
    for( int32_t i = 0; i < sz; ++i )
    {
        if( SDL_strcmp( _sprites.At( i ).name, name ) == 0 )
            return i;
    }

    SDL_LogError( CR_LOG_CATEGORY_ASSET_TEXTURE, "crSpriteAtlas: sprite not found( %s )", name );
    return -1;
}

bool crSpriteAtlas::ParseAtlasCsv( const char* path )
{
    size_t size = 0;
    char*  text = static_cast<char*>( SDL_LoadFile( path, &size ) );   // null-terminated by SDL
    if( text == nullptr )
    {
        SDL_LogError( CR_LOG_CATEGORY_ASSET_TEXTURE, "crSpriteAtlas: cratlas load failed( %s )", path );
        return false;
    }
    if( crText::HasUtf8Bom( text, path, CR_LOG_CATEGORY_ASSET_TEXTURE ) )
    {
        SDL_free( text );
        return false;
    }

    const float invW = 1.0f / static_cast<float>( _textureSize.x );
    const float invH = 1.0f / static_cast<float>( _textureSize.y );

    char* saveLine = nullptr;
    for( char* line = SDL_strtok_r( text, "\r\n", &saveLine ); line != nullptr; line = SDL_strtok_r( nullptr, "\r\n", &saveLine ) )
    {
        if( line[ 0 ] == ',' )
            continue;   // header row starts with the unnamed index column

        // row: index,name,x,y,w,h,pivotX,pivotY
        char* saveField = nullptr;
        char* fields[ 8 ] = {};

        int32_t count = 0;
        for( char* field = SDL_strtok_r( line, ",", &saveField ); ( field != nullptr ) && ( count < 8 ); field = SDL_strtok_r( nullptr, ",", &saveField ) )
        {
            fields[ count ] = field;
            ++count;
        }

        if( count != 8 )
        {
            SDL_LogError( CR_LOG_CATEGORY_ASSET_TEXTURE, "crSpriteAtlas: bad row( %s, fields:%d )", path, count );
            continue;
        }

        const float x = static_cast<float>( SDL_atoi( fields[ 2 ] ) );
        const float y = static_cast<float>( SDL_atoi( fields[ 3 ] ) );
        const float w = static_cast<float>( SDL_atoi( fields[ 4 ] ) );
        const float h = static_cast<float>( SDL_atoi( fields[ 5 ] ) );

        crAtlasSprite sprite = {};
        if( SDL_strlcpy( sprite.name, fields[ 1 ], sizeof( sprite.name ) ) >= sizeof( sprite.name ) )
        {
            SDL_LogError( CR_LOG_CATEGORY_ASSET_TEXTURE, "crSpriteAtlas: sprite name too long( %s ) — max %d chars", fields[ 1 ], static_cast<int32_t>( sizeof( sprite.name ) - 1 ) );
            SDL_assert( false );
        }
        sprite.uvMin  = float2( x * invW, y * invH );
        sprite.uvMax  = float2( ( x + w ) * invW, ( y + h ) * invH );
        sprite.sizePx = float2( w, h );
        sprite.pivot  = float2( static_cast<float>( SDL_atof( fields[ 6 ] ) ), static_cast<float>( SDL_atof( fields[ 7 ] ) ) );

        _sprites.Add( sprite );
    }

    SDL_free( text );
    return ( _sprites.Size() > 0 );
}
