#include "ColorSetup.hpp"

#include "StringUtils.hpp"
#include "text/format.h"

#ifdef __ultrix__
#include "Ultrix-Compatibility.hpp"
#endif

const ColorSetup ColorSetup::BUILT_IN= ColorSetup( "no=00:fi=00:di=01;34:ln=01:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:ex=01;31:*.tar=00;31:*.tgz=00;31:*.rpm=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31", false);

const string ColorSetup::BRIGHTER= "01";

ColorSetup::ColorSetup() {
  char* setup_= getenv( "LS_COLORS" );
  if (setup_ != NULL) 
    *this= ColorSetup( setup_);
  else
    *this= BUILT_IN;
}
  
ColorSetup::ColorSetup( const string& setup, bool init_from_built_in)
{
  if (init_from_built_in)
    *this= BUILT_IN;
    
  code_str= setup;
  do_colorization= true;

  left_code= string(1,ESC) + "[";
  right_code= "m";
 
  // The color setup is a ':'-separated sequence of strings like li=01;30 .

  StringTokenizer ST( code_str, ":", "^\\");
  while (ST.hasMoreTokens()) {
    string token= ST.getNextToken();

    StringTokenizer ST2( token, "=", "^\\");
    
    string descr= ST2.getNextToken();
    if (!ST2.hasMoreTokens()) continue;
    string value= ST2.getNextToken();
    
    if      (descr=="no")  color_text= value;
    else if (descr=="fi")  color_file= value;
    else if (descr=="di")  color_dir= value;
    else if (descr=="ex")  color_exec= value;
    else if (descr=="ln")  color_symlink= value;
    else if (descr=="bd")  color_blockdev= value;
    else if (descr=="cd")  color_chardev= value;
    else if (descr=="pi")  color_pipe= value;
    else if (descr=="so")  color_socket= value;
  } // while
  
  // "intelligent" setup for default color value
  if (! color_text.empty())
    color_default= color_text;
  else
    color_default= "00";
    
  if (end_code.empty())
    end_code= left_code + color_default + right_code;

}

string ColorSetup::format( string st, const string & color_str, bool colored)  const {
  st= text::clean( st);
  if (colored) {
    st.insert( 0, left_code + color_str + right_code);
    st.append( end_code);
  }
  return st;
}

string ColorSetup::formatFilename( const UnixFile& f)  const {
  if (!do_colorization)  return f.getName();
  
  string col;
  // now decide which color code to use. The following statements' order is
  // very important!!!
  if (f.isNavigable())  col= color_dir;
  else 
    switch (f.getType()) {
      case UnixFile::SYMLINK: col= color_symlink;  break;
      case UnixFile::BLOCK_DEVICE: col= color_blockdev; break;
      case UnixFile::CHAR_DEVICE: col= color_chardev; break;
      case UnixFile::PIPE: col= color_pipe; break;
      case UnixFile::SOCKET: col= color_socket; break;
      default:      
        if (f.isExecutable())  col= color_exec;
        else {  // look for color code suitable for the file's extension:
          string filename= f.getName();
          string::size_type pos= filename.rfind('.');
          if (pos!=string::npos) {
            string extension= filename.substr( pos) + '=';  // dot, extension and '='
                // now search the complete LS_COLORS string for the extension.
            string::size_type code_pos= this->code_str.find( extension);
            if (code_pos!=string::npos) {
              StringTokenizer st( code_str, "=:", "^\\", code_pos + extension.size());
              if (st.hasMoreTokens()) {
                col= st.getNextToken();
              }
            }
          }
        }
    } // switch
  if (col.empty()) col= color_file;    // no suitable file type found yet.
  if (col.empty()) col= color_default; 
  
  return format( f.getName(), col);
}

