/* -*- C++ -*-
 * 
 *  ONScripter_animation.cpp - Methods to manipulate AnimationInfo
 *
 *  Copyright (c) 2001-2012 Ogapee. All rights reserved.
 *
 *  ogapee@aqua.dti2.ne.jp
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "ONScripter.h"

int ONScripter::proceedAnimation()
{
    int i, min = -1; // minimum duration
    AnimationInfo *anim;
    
    for ( i=0 ; i<3 ; i++ ){
        anim = &tachi_info[i];
        if ( anim->visible && anim->is_animatable ){
            if (min == -1 || min > anim->remaining_time)
                min = anim->remaining_time;
        }
    }

    for ( i=MAX_SPRITE_NUM-1 ; i>=0 ; i-- ){
        anim = &sprite_info[i];
        if ( anim->visible && anim->is_animatable ){
            if (min == -1 || min > anim->remaining_time)
                min = anim->remaining_time;
        }
    }

    if ( !textgosub_label &&
         ( clickstr_state == CLICK_WAIT ||
           clickstr_state == CLICK_NEWPAGE ) ){
        
        if      ( clickstr_state == CLICK_WAIT )
            anim = &cursor_info[0];
        else if ( clickstr_state == CLICK_NEWPAGE )
            anim = &cursor_info[1];

        if ( anim->visible && anim->is_animatable ){
            if (min == -1 || min > anim->remaining_time)
                min = anim->remaining_time;
        }
    }

#ifdef USE_LUA
    if (lua_handler.is_animatable && !script_h.isExternalScript()){
        if (min == -1 || min > lua_handler.remaining_time)
            min = lua_handler.remaining_time;
    }
#endif

    if ( min == -1 ) min = 0;

    return min;
}

void ONScripter::resetRemainingTime( int t )
{
    int i;
    AnimationInfo *anim;
    
    for ( i=0 ; i<3 ; i++ ){
        anim = &tachi_info[i];
        if ( anim->visible && anim->is_animatable){
            if (anim->proceedAnimation(t))
                flushDirect( anim->pos, refreshMode() | (draw_cursor_flag?REFRESH_CURSOR_MODE:0) );
        }
    }
        
    for ( i=MAX_SPRITE_NUM-1 ; i>=0 ; i-- ){
        anim = &sprite_info[i];
        if ( anim->visible && anim->is_animatable ){
            if (anim->proceedAnimation(t))
                flushDirect( anim->pos, refreshMode() | (draw_cursor_flag?REFRESH_CURSOR_MODE:0) );
        }
    }
#ifdef USE_LUA
    if (lua_handler.is_animatable && !script_h.isExternalScript()){
        lua_handler.remaining_time -= t;

        if (lua_handler.remaining_time == 0){
            lua_handler.remaining_time = lua_handler.duration_time;

            int lua_event_mode = event_mode;
            int tmp_string_buffer_offset = string_buffer_offset;
            char *current = script_h.getCurrent();
            lua_handler.callback(LUAHandler::LUA_ANIMATION);
            script_h.setCurrent(current);
            readToken();
            string_buffer_offset = tmp_string_buffer_offset;
            event_mode = lua_event_mode;
        }
    }
#endif
    if ( !textgosub_label &&
         ( clickstr_state == CLICK_WAIT ||
           clickstr_state == CLICK_NEWPAGE ) ){
        if ( clickstr_state == CLICK_WAIT )
            anim = &cursor_info[0];
        else if ( clickstr_state == CLICK_NEWPAGE )
            anim = &cursor_info[1];
        
        if ( anim->visible && anim->is_animatable ){
            if (anim->proceedAnimation(t)){
                SDL_Rect dst_rect = anim->pos;
                if ( !anim->abs_flag ){
                    dst_rect.x += sentence_font.x() * screen_ratio1 / screen_ratio2;
                    dst_rect.y += sentence_font.y() * screen_ratio1 / screen_ratio2;
                }
                flushDirect( dst_rect, refreshMode() | (draw_cursor_flag?REFRESH_CURSOR_MODE:0) );
            }
        }
    }
}

void ONScripter::setupAnimationInfo( AnimationInfo *anim, FontInfo *info )
{
    if (anim->trans_mode != AnimationInfo::TRANS_STRING &&
        anim->file_name && anim->surface_name &&
        strcmp(anim->file_name, anim->surface_name) == 0 &&
        ((!anim->mask_file_name && !anim->mask_surface_name) ||
         (anim->mask_file_name && !anim->mask_surface_name &&
          strcmp(anim->mask_file_name, anim->mask_surface_name) == 0))) return;

    anim->deleteSurface();
    anim->abs_flag = true;

    if ( anim->surface_name ) delete[] anim->surface_name;
    anim->surface_name = new char[ strlen(anim->file_name) + 1 ];
    strcpy( anim->surface_name, anim->file_name );

    if (anim->mask_surface_name) delete[] anim->mask_surface_name;
    if (anim->mask_file_name){
        anim->mask_surface_name = new char[ strlen(anim->mask_file_name) + 1 ];
        strcpy( anim->mask_surface_name, anim->mask_file_name );
    }
    else{
        anim->mask_surface_name = NULL;
    }
    
    if ( anim->trans_mode == AnimationInfo::TRANS_STRING ){
        FontInfo f_info = sentence_font;
        if (info) f_info = *info;
        f_info.rubyon_flag = anim->is_ruby_drawable;

        if ( anim->font_size_xy[0] >= 0 ){ // in case of Sprite, not rclick menu
            f_info.setTateyokoMode(0);
            f_info.top_xy[0] = anim->orig_pos.x;
            f_info.top_xy[1] = anim->orig_pos.y;
            if (anim->is_single_line)
                f_info.setLineArea( strlen(anim->file_name)/2+1 );
            f_info.clear();
            
            f_info.pitch_xy[0] = f_info.pitch_xy[0] - f_info.font_size_xy[0] + anim->font_size_xy[0];
            f_info.font_size_xy[0] = anim->font_size_xy[0];
            f_info.pitch_xy[1] = f_info.pitch_xy[1] - f_info.font_size_xy[1] + anim->font_size_xy[1];
            f_info.font_size_xy[1] = anim->font_size_xy[1];
            if ( anim->font_pitch >= 0 )
                f_info.pitch_xy[0] = anim->font_pitch;
            f_info.ttf_font = NULL;
        }

        SDL_Rect pos;
        if (anim->is_tight_region){
            drawString( anim->file_name, anim->color_list[ anim->current_cell ], &f_info, false, NULL, &pos );
        }
        else{
            int xy_bak[2];
            xy_bak[0] = f_info.xy[0];
            xy_bak[1] = f_info.xy[1];
            
            int xy[2] = {0, 0};
            f_info.setXY(f_info.num_xy[0]-1, f_info.num_xy[1]-1);
            pos = f_info.calcUpdatedArea(xy);

            f_info.xy[0] = xy_bak[0];
            f_info.xy[1] = xy_bak[1];
        }
        
        if (info != NULL){
            info->xy[0] = f_info.xy[0];
            info->xy[1] = f_info.xy[1];
        }
        
        anim->orig_pos.w = pos.w;
        anim->orig_pos.h = pos.h;
        anim->scalePosWH( screen_ratio1, screen_ratio2 );
        anim->allocImage( anim->pos.w*anim->num_of_cells, anim->pos.h, texture_format );
        anim->fill( 0, 0, 0, 0 );
        
        f_info.top_xy[0] = f_info.top_xy[1] = 0;
        for ( int i=0 ; i<anim->num_of_cells ; i++ ){
            f_info.clear();
            drawString( anim->file_name, anim->color_list[i], &f_info, false, NULL, NULL, anim );
            f_info.top_xy[0] += anim->orig_pos.w;
        }
    }
    else{
        bool has_alpha;
        int location;
        SDL_Surface *surface = loadImage( anim->file_name, &has_alpha, &location );

        SDL_Surface *surface_m = NULL;
        if (anim->trans_mode == AnimationInfo::TRANS_MASK)
            surface_m = loadImage( anim->mask_file_name );
        
        surface = anim->setupImageAlpha(surface, surface_m, has_alpha);

        if (surface &&
            screen_ratio2 != screen_ratio1 &&
            (!disable_rescale_flag || location == BaseReader::ARCHIVE_TYPE_NONE))
        {
            SDL_Surface *src_s = surface;

            int w, h;
            if ( (w = src_s->w * screen_ratio1 / screen_ratio2) == 0 ) w = 1;
            if ( (h = src_s->h * screen_ratio1 / screen_ratio2) == 0 ) h = 1;
            SDL_PixelFormat *fmt = image_surface->format;
            surface = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h,
                                            fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask );
        
            resizeSurface(src_s, surface);
            SDL_FreeSurface(src_s);
        }

        anim->setImage( surface, texture_format );

        if ( surface_m ) SDL_FreeSurface(surface_m);
    }
}

void ONScripter::parseTaggedString( AnimationInfo *anim )
{
    if (anim->image_name == NULL) return;
    anim->removeTag();
    
    int i;
    char *buffer = anim->image_name;
    anim->num_of_cells = 1;
    anim->trans_mode = trans_mode;

    if ( buffer[0] == ':' ){
        while (*++buffer == ' ');
        
        if ( buffer[0] == 'a' ){
            anim->trans_mode = AnimationInfo::TRANS_ALPHA;
            buffer++;
        }
        else if ( buffer[0] == 'l' ){
            anim->trans_mode = AnimationInfo::TRANS_TOPLEFT;
            buffer++;
        }
        else if ( buffer[0] == 'r' ){
            anim->trans_mode = AnimationInfo::TRANS_TOPRIGHT;
            buffer++;
        }
        else if ( buffer[0] == 'c' ){
            anim->trans_mode = AnimationInfo::TRANS_COPY;
            buffer++;
        }
        else if ( buffer[0] == 's' ){
            anim->trans_mode = AnimationInfo::TRANS_STRING;
            buffer++;
            anim->num_of_cells = 0;
            if ( *buffer == '/' ){
                buffer++;
                script_h.getNext();
                
                script_h.pushCurrent( buffer );
                anim->font_size_xy[0] = script_h.readInt();
                anim->font_size_xy[1] = script_h.readInt();
                if ( script_h.getEndStatus() & ScriptHandler::END_COMMA ){
                    anim->font_pitch = script_h.readInt() + anim->font_size_xy[0];
                    if ( script_h.getEndStatus() & ScriptHandler::END_COMMA ){
                        script_h.readInt(); // 0 ... normal, 1 ... no anti-aliasing, 2 ... Fukuro
                    }
                }
                else{
                    anim->font_pitch = anim->font_size_xy[0];
                }
                buffer = script_h.getNext();
                script_h.popCurrent();
            }
            else{
                anim->font_size_xy[0] = sentence_font.font_size_xy[0];
                anim->font_size_xy[1] = sentence_font.font_size_xy[1];
                anim->font_pitch = sentence_font.pitch_xy[0];
            }
            while(buffer[0] != '#' && buffer[0] != '\0') buffer++;
            i=0;
            while( buffer[i] == '#' ){
                anim->num_of_cells++;
                i += 7;
            }
            anim->color_list = new uchar3[ anim->num_of_cells ];
            for ( i=0 ; i<anim->num_of_cells ; i++ ){
                readColor( &anim->color_list[i], buffer );
                buffer += 7;
            }
        }
        else if ( buffer[0] == 'm' ){
            anim->trans_mode = AnimationInfo::TRANS_MASK;
            char *start = ++buffer;
            while(buffer[0] != ';' && buffer[0] != 0x0a && buffer[0] != '\0') buffer++;
            if (buffer[0] == ';')
                setStr( &anim->mask_file_name, start, buffer-start );
        }
        else if ( buffer[0] == '#' ){
            anim->trans_mode = AnimationInfo::TRANS_DIRECT;
            readColor( &anim->direct_color, buffer );
            buffer += 7;
        }
        else if ( buffer[0] == '!' ){
            anim->trans_mode = AnimationInfo::TRANS_PALLETTE;
            buffer++;
            anim->pallette_number = getNumberFromBuffer( (const char**)&buffer );
        }

        if (anim->trans_mode != AnimationInfo::TRANS_STRING)
            while(buffer[0] != '/' && buffer[0] != ';' && buffer[0] != '\0') buffer++;
    }

    if ( buffer[0] == '/' && anim->trans_mode != AnimationInfo::TRANS_STRING){
        buffer++;
        anim->num_of_cells = getNumberFromBuffer( (const char**)&buffer );
        buffer++;
        if ( anim->num_of_cells == 0 ){
            fprintf( stderr, "ONScripter::parseTaggedString  The number of cells is 0\n");
            return;
        }

        anim->duration_list = new int[ anim->num_of_cells ];

        if ( *buffer == '<' ){
            buffer++;
            for ( i=0 ; i<anim->num_of_cells ; i++ ){
                anim->duration_list[i] = getNumberFromBuffer( (const char**)&buffer );
                buffer++;
            }
            buffer++; // skip '>'
        }
        else{
            anim->duration_list[0] = getNumberFromBuffer( (const char**)&buffer );
            for ( i=1 ; i<anim->num_of_cells ; i++ )
                anim->duration_list[i] = anim->duration_list[0];
            buffer++;
        }
        anim->remaining_time = anim->duration_list[0];
        
        anim->loop_mode = *buffer++ - '0'; // 3...no animation
        if ( anim->loop_mode != 3 ) anim->is_animatable = true;

        while(buffer[0] != ';' && buffer[0] != '\0') buffer++;
    }

    if ( buffer[0] == ';' && anim->trans_mode != AnimationInfo::TRANS_STRING) buffer++;

    if ( anim->trans_mode == AnimationInfo::TRANS_STRING && buffer[0] == '$' ){
        script_h.pushCurrent( buffer );
        setStr( &anim->file_name, script_h.readStr() );
        script_h.popCurrent();
    }
    else{
        setStr( &anim->file_name, buffer );
    }
}

void ONScripter::drawTaggedSurface( SDL_Surface *dst_surface, AnimationInfo *anim, SDL_Rect &clip )
{
    SDL_Rect poly_rect = anim->pos;
    if ( !anim->abs_flag ){
        poly_rect.x += sentence_font.x() * screen_ratio1 / screen_ratio2;
        poly_rect.y += sentence_font.y() * screen_ratio1 / screen_ratio2;
    }

    if (!anim->affine_flag)
        anim->blendOnSurface( dst_surface, poly_rect.x, poly_rect.y,
                              clip, anim->trans );
    else
        anim->blendOnSurface2( dst_surface, poly_rect.x, poly_rect.y,
                               clip, anim->trans );
}

void ONScripter::stopAnimation( int click )
{
    int no;

    if ( textgosub_label ) return;

    if      ( click == CLICK_WAIT )    no = 0;
    else if ( click == CLICK_NEWPAGE ) no = 1;
    else return;

    if (cursor_info[no].image_surface == NULL) return;
    
    SDL_Rect dst_rect = cursor_info[ no ].pos;

    if ( !cursor_info[ no ].abs_flag ){
        dst_rect.x += sentence_font.x() * screen_ratio1 / screen_ratio2;
        dst_rect.y += sentence_font.y() * screen_ratio1 / screen_ratio2;
    }

    flushDirect( dst_rect, refreshMode() );
}
