/***********************************************************************
* sgr_optimizer.cpp - Combines SGR attributes                          *
*                                                                      *
* This file is part of the FINAL CUT widget toolkit                    *
*                                                                      *
* Copyright 2019-2021 Markus Gans                                      *
*                                                                      *
* FINAL CUT is free software; you can redistribute it and/or modify    *
* it under the terms of the GNU Lesser General Public License as       *
* published by the Free Software Foundation; either version 3 of       *
* the License, or (at your option) any later version.                  *
*                                                                      *
* FINAL CUT 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 Lesser General Public License for more details.                  *
*                                                                      *
* You should have received a copy of the GNU Lesser General Public     *
* License along with this program.  If not, see                        *
* <http://www.gnu.org/licenses/>.                                      *
***********************************************************************/
#include <iostream>
#include <cstring>
#include <vector>

#include "final/fc.h"
#include "final/output/tty/sgr_optimizer.h"

namespace finalcut
{

//----------------------------------------------------------------------
// class SGRoptimizer
//----------------------------------------------------------------------

// constructors and destructor
//----------------------------------------------------------------------
SGRoptimizer::SGRoptimizer (std::string& sequence)
  : seq{sequence}
{
  seq.reserve(ATTR_BUF_SIZE);
}


// public methods of SGRoptimizer
//----------------------------------------------------------------------
void SGRoptimizer::optimize()
{
  findParameter();
  combineParameter();
}


// private methods of SGRoptimizer
//----------------------------------------------------------------------
void SGRoptimizer::findParameter()
{
  // Find ANSI X3.64 terminal SGR (Select Graphic Rendition) strings

  const std::size_t len = seq.length();
  csi_parameter.clear();

  if ( len < 6 )
    return;

  std::size_t start{NOT_SET};
  bool esc{false};
  bool csi{false};

  // Find SGR parameter
  for (std::size_t i = 0; i < len; i++)
  {
    if ( csi )
    {
      if ( start == NOT_SET )
        start = i;

      if ( (seq[i] >= '0' && seq[i] <= '9') || seq[i] == ';' )
        continue;

      if ( seq[i] == 'm')
      {
        csi_parameter.push_back({start, i});
      }

      esc = csi = false;
      start = NOT_SET;
    }

    // Other content
    if ( ! csi_parameter.empty() && i > csi_parameter.back().end + 2 )
      break;

    if ( esc && seq[i] == '[' )  // Esc [
      csi = true;

    if ( seq[i] == ESC[0] )  // Esc
      esc = true;
  }
}

//----------------------------------------------------------------------
void SGRoptimizer::combineParameter()
{
  // Combine SGR (Select Graphic Rendition) attributes

  if ( csi_parameter.size() < 2 )
    return;

  const auto& first = csi_parameter.front();
  const std::size_t len = seq.length();
  std::size_t count = 1;
  std::size_t read_pos{};
  std::size_t write_pos = first.end;

  if ( first.start == first.end )  // Esc [ m
  {
    seq[write_pos] = '0';
    write_pos++;
  }

  seq[write_pos] = ';';
  write_pos++;
  const auto& begin = csi_parameter.cbegin() + 1;
  const auto& end = csi_parameter.cend();

  for (auto&& p : std::vector<parameter>(begin, end))
  {
    count++;

    for (read_pos = p.start; read_pos <= p.end; read_pos++)
    {
      if ( seq[read_pos] == 'm' )
      {
        if ( p.start == p.end )  // Esc [ m
        {
          seq[write_pos] = '0';
          write_pos++;
        }

        if ( count == csi_parameter.size() )
          seq[write_pos] = 'm';
        else
          seq[write_pos] = ';';
      }
      else
        seq[write_pos] = seq[read_pos];

      write_pos++;
    }
  }

  while ( read_pos < len )
  {
    seq[write_pos] = seq[read_pos];
    read_pos++;
    write_pos++;
  }

  seq.erase(write_pos);
}

}  // namespace finalcut

