/*
 *
 * $Id: k3bpatternparser.cpp 619556 2007-01-03 17:38:12Z trueg $
 * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
 * Copyright (C) 2004-2005 Jakob Petsovits <jpetso@gmx.at>
 *
 * This file is part of the K3b project.
 * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
 *
 * 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.
 * See the file "COPYING" for the exact licensing terms.
 */


#include "k3bpatternparser.h"

#include <tqregexp.h>
#include <tqdatetime.h>
#include <tqvaluestack.h>

#include <kglobal.h>
#include <klocale.h>


TQString K3bPatternParser::parsePattern( const K3bCddbResultEntry& entry,
                                        unsigned int trackNumber,
                                        const TQString& pattern,
                                        bool replace,
                                        const TQString& replaceString )
{
  if( entry.titles.count() < trackNumber )
    return "";

  TQString dir, s;
  char c = ' ';     // contains the character representation of a special string
  unsigned int len; // length of the current special string


  for( unsigned int i = 0; i < pattern.length(); ++i ) {

    if( pattern[i] == '%' ) {

      if( i + 1 < pattern.length() ) {
        len = 2;

        if( pattern[i+1] != '{' ) {  // strings like %a
          c = pattern[i+1];
        }
        else if( i + 3 >= pattern.length() ) {  // too short to contain a %{*} string
          c = ' ';
        }
        else {  // long enough to contain %{*}

          if( pattern[i+3] == '}' ) {  // strings like %{a}
            c = pattern[i+2];
            len = 4;
          }
          else {  // strings like %{artist}, or anything like %{*

            while( i + len - 1 < pattern.length() ) {
              ++len;

              if( pattern[i + len - 1] == '%' ) {  // don't touch other special strings
                c = ' ';
                --len;
                break;
              }
              else if( pattern[i + len - 1] == '}' ) {
                s = pattern.mid( i + 2, len - 3 );

                if( s == "title" ) {
                  c = TITLE;
                }
                else if( s == "artist" ) {
                  c = ARTIST;
                }
                else if( s == "number" ) {
                  c = NUMBER;
                }
                else if( s == "comment" ) {
                  c = COMMENT;
                }
                else if( s == "year" ) {
                  c = YEAR;
                }
                else if( s == "genre" ) {
                  c = GENRE;
                }
                else if( s == "albumtitle" ) {
                  c = ALBUMTITLE;
                }
                else if( s == "albumartist" ) {
                  c = ALBUMARTIST;
                }
                else if( s == "albumcomment" ) {
                  c = ALBUMCOMMENT;
                }
                else if( s == "date" ) {
                  c = DATE;
                }
                else {  // no valid pattern in here, don't replace anything
                  c = ' ';
                }
                break; // finished parsing %{* string
              }
            } // end of while(...)

          } // end of %{* strings

        } // end of if( long enough to contain %{*} )

        switch( c ) {
          case ARTIST:
            s = entry.artists[trackNumber-1];
            s.replace( '/', '_' );
            s.replace( '*', '_' );
            s.replace( '}', '*' );  // for conditional inclusion
            dir.append( s.isEmpty()
                ? i18n("unknown") + TQString(" %1").arg(trackNumber)
                : s );
            break;
          case TITLE:
            s = entry.titles[trackNumber-1];
            s.replace( '/', '_' );
            s.replace( '*', '_' );
            s.replace( '}', '*' );
            dir.append( s.isEmpty()
                ? i18n("Track %1").arg(trackNumber)
                : s );
            break;
          case NUMBER:
            dir.append( TQString::number(trackNumber).rightJustify( 2, '0' ) );
            break;
          case YEAR:
            dir.append( TQString::number( entry.year ) );
            break;
          case COMMENT:
            s = entry.extInfos[trackNumber-1];
            s.replace( '/', '_' );
            s.replace( '*', '_' );
            s.replace( '}', '*' );
            dir.append( s );
            break;
          case GENRE:
            s = ( entry.genre.isEmpty() ? entry.category : entry.genre );
            s.replace( '/', '_' );
            s.replace( '*', '_' );
            s.replace( '}', '*' );
            dir.append( s );
            break;
          case ALBUMARTIST:
            dir.append( entry.cdArtist.isEmpty()
                ? i18n("unknown") : entry.cdArtist );
            break;
          case ALBUMTITLE:
            s = entry.cdTitle;
            s.replace( '/', '_' );
            s.replace( '*', '_' );
            s.replace( '}', '*' );
            dir.append( s.isEmpty()
                ? i18n("unknown") : s );
            break;
          case ALBUMCOMMENT:
            s = entry.cdExtInfo;
            s.replace( '/', '_' );
            s.replace( '*', '_' );
            s.replace( '}', '*' );
            dir.append( s ); // I think it makes more sense to allow empty comments
            break;
          case DATE:
            dir.append( KGlobal::locale()->formatDate( TQDate::currentDate() ) );
            break;
          default:
            dir.append( pattern.mid(i, len) );
            break;
        }
        i += len - 1;
      }
      else {  // end of pattern
        dir.append( "%" );
      }
    }
    else {
      dir.append( pattern[i] );
    }
  }



  // /* delete line comment to comment out
  // the following part: Conditional Inclusion

  TQValueStack<int> offsetStack;
  TQString inclusion;
  bool isIncluded;

  static TQRegExp conditionrx( "^[@|!][atyegrmx](?:='.*')?\\{" );
  conditionrx.setMinimal( TRUE );

  for( unsigned int i = 0; i < dir.length(); ++i ) {

    offsetStack.push(
      conditionrx.search(dir, i, TQRegExp::CaretAtOffset) );

    if( offsetStack.top() == -1 ) {
      offsetStack.pop();
    }
    else {
      i += conditionrx.matchedLength() - 1;
      continue;
    }

    if( dir[i] == '}' && !offsetStack.isEmpty() ) {

      int offset = offsetStack.pop();
      int length = i - offset + 1;

      switch( (TQChar) dir[offset+1] ) {
      case ARTIST:
        s = entry.artists[trackNumber-1];
        break;
      case TITLE:
        s = entry.titles[trackNumber-1];
        break;
      case NUMBER:
        s = TQString::number( trackNumber );
        break;
      case YEAR:
        s = TQString::number( entry.year );
        break;
      case COMMENT:
        s = entry.extInfos[trackNumber-1];
        break;
      case GENRE:
        s = ( entry.genre.isEmpty() ? entry.category : entry.genre );
        break;
      case ALBUMARTIST:
        s = entry.cdArtist;
        break;
      case ALBUMTITLE:
        s = entry.cdTitle;
        break;
      case ALBUMCOMMENT:
        s = entry.cdExtInfo;
        break;
      case DATE:
        s = KGlobal::locale()->formatDate( TQDate::currentDate() );
        break;
      default: // we must never get here,
        break; // all choices should be covered
      }

      if( dir[offset+2] == '{' ) { // no string matching, e.g. ?y{text}
        switch( (TQChar) dir[offset+1] ) {
        case YEAR:
          isIncluded = (s != "0");
          break;
        default:
          isIncluded = !s.isEmpty();
          break;
        }
        inclusion = dir.mid( offset + 3, length - 4 );
      }
      else { // with string matching, e.g. ?y='2004'{text}

	// Be aware that there might be ' in the condition text
        int endOfCondition = dir.find( '{', offset+4 )-1;
        TQString condition = dir.mid( offset+4,
				     endOfCondition - (offset+4) );

        isIncluded = (s == condition);
        inclusion = dir.mid( endOfCondition+2,
                             i - (endOfCondition+2) );
      }

      if( dir[offset] == '!' )
          isIncluded = !isIncluded;
      // Leave it when it's '@'.

      dir.replace( offset, length, ( isIncluded ? inclusion : TQString("") ) );

      if( isIncluded == TRUE )
        i -= length - inclusion.length();
      else
        i = offset - 1; // start next loop at offset

      continue;

    } // end of replace (at closing bracket '}')

  } // end of conditional inclusion for(...)

  // end of Conditional Inclusion */


  dir.replace( '*', '}' );  // bring the brackets back, if there were any

  if( replace )
    dir.replace( TQRegExp( "\\s" ), replaceString );

  return dir;
}