/* ====================================================================
* Copyright (c) 2003-2006, Martin Hauner
*                          http://subcommander.tigris.org
*
* Subcommander is licensed as described in the file doc/COPYING, which
* you should have received as part of this distribution.
* ====================================================================
*/

// sc
#include "PaintLineFactory.h"
#include "PaintLine.h"
#include "LineConfig.h"
#include "Tab.h"
#include "util/String.h"
#include "util/Char.h"
#include "util/utf8.h"


const PaintLine* PaintLineFactory::create( const sc::String& source,
  int leftCol, int rightCol, const LineConfig& cfg )
{
  // we use cols here to have enough space for tabs.
  // each column can contain a 4 byte utf character.
  // plus 1 byte for the end marker.

  Tab tab(cfg.getTabWidth());

  // return/linefeed doesn't count as column, so add 1
  unsigned int cols = tab.calcColumns( source ) + 1;

#if 0
  // safety check, always return a "valid" PaintLine
  if( cols == 0 )
  {
    PaintLine::Kind* kind  = new PaintLine::Kind[2];
    kind[0] = PaintLine::Whitespace;
    kind[1] = PaintLine::End;

    return new PaintLine( source, sc::String("?"), kind );
  }
#endif

  const char*      src   = source;
  char*            buf   = new char[cols*4+2];
  PaintLine::Kind* kind  = new PaintLine::Kind[cols*4+2];

  int         ccol  = 0; // column
  int         cbyte = 0; // byte offset

  while( src && *src )
  {
    int  addedbytes = 0;
    int  addedcols  = addChar( src, buf+cbyte, ccol, &addedbytes, cfg );
    bool whitespace = sc::Char::isWhitespace(*src);

    for( int i = 0; i < addedbytes; i++ )
    {
      if( ccol >= leftCol && ccol < rightCol )
      {
        // highlighted
        kind[cbyte+i] = whitespace ? PaintLine::WhitespaceHL : PaintLine::CharacterHL; 
      }
      else
      {
        // normal
        kind[cbyte+i] = whitespace ? PaintLine::Whitespace   : PaintLine::Character; 
      }
    }

    ccol  += addedcols;
    cbyte += addedbytes;

    src = utf8::next8(src);
  }

  buf[cbyte]  = 0;
  kind[cbyte] = PaintLine::End;

  sc::String bufstr(buf);
  delete [] buf;

  return new PaintLine( source, bufstr, kind );
}


/**
* \brief add char from in to out and translate tabs and whitespaces.
*
* \param  in     source string.
* \param  out    destination string.
* \param  col    current column. col is needed for tab handling.
* \param  bytes  number of bytes copied from in to out.
* \param cfg     the line configuration.
* \return number of columns copied.
*/

int PaintLineFactory::addChar( const char* in, char* out, int col, int* bytes,
                              const LineConfig& cfg )
{
  switch( *in )
  {
  case ' ':
    {
      if( cfg.isVisibleSpace() )
      {
        out[0] = cfg.getCharSpace();
      }
      else
      {
        out[0] = *in;
      }
      *bytes = 1;
      return 1;
    }
  case '\t':
    {
      if( cfg.isVisibleTab() )
      {
        out[0] = cfg.getCharTab();
      }
      else
      {
        out[0] = ' ';
      }

      int retval = 1;

      for( int t = 1; (col+t)%cfg.getTabWidth() != 0; t++ )
      {
        out[t] = ' ';
        retval++;
      }
      *bytes = retval;
      return retval;
    }
  case '\r':
    {
      if( cfg.isVisibleCariageReturn() )
      {
        out[0] = cfg.getCharCariageReturn();
        *bytes = 1;
        return 1;
      }
      *bytes = 0;
      return 0;
    }
  case '\n':
    {
      if( cfg.isVisibleLineFeed() )
      {
        out[0] = cfg.getCharLineFeed();
        *bytes = 1;
        return 1;
      }
      *bytes = 0;
      return 0;
    }
  default:
    {
      const char* n = utf8::next8( in );
      int i = 0;
      for( /*i*/; in+i < n; i++ )
      {
        out[i] = in[i];
      }
      *bytes = i;
      return 1;
    }
  }
}
