/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2023  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
#include "stoolkit/syntax/SPattern.h"
#include "stoolkit/SCharClass.h"

/**
 * A sample pattern that can be extended. This pattern 
 * now highlights the following:
 *
 * 123... number 
 * Yuko   keyword
 * yuko   error
 * YUKO (kanji) define
 * characters none
 * other  control
 *
 * @author Gaspar Sinai
 * @version 2007-11-27
 */
SPattern::SPattern (void) :
ACT_NONE ("none"), 
ACT_ERROR ("error"), 
ACT_NUMBER ("number"), 
ACT_KEYWORD ("keyword"), 
ACT_VARIABLE ("variable"), 
ACT_DEFINE ("define"), 
ACT_CONTROL ("control"),
ACT_OTHER ("other")
{
  clear ();
}

SPattern::~SPattern ()
{
}

//
void
SPattern::clear ()
{
  action = ACT_NONE;
  matchBegin = SD_MATCH_EOD;
  matchEnd = 0;
  next = 0;
  current.clear ();
}

// This method will be overwritten
bool
SPattern::checkMatch ()
{
  return checkMatchTest();
}

void
SPattern::append (SS_UCS4 in)
{
  if (next == 0) matchEnd = 0;
  current.append (in);
  next++;
}

// We all ignore the last character in the buffer,
// that is next position, and it is non-inclusive
// note that current position is next - 1, and
// we dont look at current.
bool
SPattern::checkMatchTest ()
{
  // We are lucky, none of our staring chars are used in the other pattern.
  if (current.size() >= 3 &&
    current[current.size()-3] == 0x512A // YUU
      && current[current.size()-2] == 0x5B50) // KO
  {
    if (singleMatchTest (3)) return true;
    current.remove(0);
    current.remove(0);

    matchBegin = next-1-2;
    matchEnd = next-1;
    action = ACT_DEFINE;
    return true;
  }
  else if (current.size() >= 2
    && current[current.size()-2] == (SS_UCS4)'\n') // YUU
  {
    if (singleMatchTest (2)) return true;
    current.remove(0);
    matchBegin = next-1-1;
    matchEnd = next-1;
    action = ACT_CONTROL;
    return true;
  }
  else if (current.size() >= 5
    && current[current.size()-5] == (SS_UCS4)'y' 
    && current[current.size()-4] == (SS_UCS4)'u' 
    && current[current.size()-3] == (SS_UCS4)'k' 
    && current[current.size()-2] == (SS_UCS4)'o') 
  {
    if (singleMatchTest (5)) return true;
    current.remove(0);
    current.remove(0);
    current.remove(0);
    current.remove(0);
    matchBegin = next-1-4;
    matchEnd = next-1;
    action = ACT_ERROR;
    return true;
  }
  else if (current.size() >= 5
    && current[current.size()-5] == (SS_UCS4)'Y' 
    && current[current.size()-4] == (SS_UCS4)'u' 
    && current[current.size()-3] == (SS_UCS4)'k' 
    && current[current.size()-2] == (SS_UCS4)'o') 
  {
    if (singleMatchTest (5)) return true;
    current.remove(0);
    current.remove(0);
    current.remove(0);
    current.remove(0);
    matchBegin = next-1-4;
    matchEnd = next-1;
    action = ACT_KEYWORD;
    return true;
  }
  bool isEOD = current.size() > 0 &&  current[current.size()-1] == SD_MATCH_EOD;
  return singleMatchTest (isEOD ? 1: 5);
}

// match current[0] and remove it.
bool
SPattern::singleMatchTest (unsigned int leaveSize)
{
  if (current.size() > leaveSize)
  {
    matchBegin = matchEnd;
    matchEnd++;
    action = ACT_NONE;
    SD_CharClass cc = getCharClass (current[0]);
    switch (cc)
    {
    // letter
    case SD_CC_Lu:
    case SD_CC_Ll:
    case SD_CC_Lt:
    case SD_CC_Lm:
    case SD_CC_Lo:
      current.remove (0);
      if (!singleMatchTest (leaveSize))
      {
        return false;
      }
      return true;
    case SD_CC_Nd:
    case SD_CC_Nl:
    case SD_CC_No:
      action = ACT_NUMBER;
      break;
    case SD_CC_So:
      action = ACT_OTHER;
      break;
    default:
      action = ACT_CONTROL;
    }
    current.remove (0);
    return true;
  }
  return false;
}
