#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/param.h>

#include <cstdio>
#include <fstream>
#include <iostream>

#include "ast_vbox.h"

using namespace std;

extern int alphasort();

static string select_path;

int dir_select( struct direct const *entry )
{
  if( (strcmp(entry->d_name, ".") == 0) ||
      (strcmp(entry->d_name, "..") == 0) )
    {
      return (FALSE);
    }
  else
    {
      struct stat state;
      int filetype;

      string fullname = select_path + "/" + string( entry->d_name );      
      stat( fullname.c_str(), &state );
      filetype = state.st_mode & S_IFMT;
      
      return ( filetype == S_IFDIR );
    }
}

int msg_select( struct direct const *entry )
{
  if( (strcmp(entry->d_name, ".") == 0) ||
      (strcmp(entry->d_name, "..") == 0) )
    {
      return (FALSE);
    }
  else
    {
      char *ext = rindex( entry->d_name, '.' );

      if( strcmp( ext, ".txt" ) == 0 )
	{
	  return (TRUE);
	}
      else
	{
	  return ( FALSE );
	}
    }
}

// create a maintainer of voiceboxes for a certain path
astVoiceboxList::astVoiceboxList( string path )
  : m_path( path )
{
  RefreshVoiceboxes();
}

// return a list of voicebox objects
// (iterate directories in paths, create voicebox instances and return list)
void astVoiceboxList::RefreshVoiceboxes()
{
  m_boxes.clear();

  // obtain all directories in m_path
  struct direct **files;
  select_path = m_path;
  int count = scandir( m_path.c_str(), &files, dir_select, alphasort );
  if( count )
    {
      for ( int i=0; i < count; i++ )
	{
	  string vb_name = string( files[i]->d_name );
	  string full_vb_path = m_path + "/" + vb_name;
	  m_boxes.push_back( astVoicebox( full_vb_path, vb_name ) );

	  free( files[i] );
	}
      free( files );
    }
  else
    {
    }
}

list<astVoicebox> astVoiceboxList::GetVoiceboxes()
{
  return m_boxes;
}


// remember path and name internally
astVoicebox::astVoicebox( std::string path, std::string name )
  : m_name( name ), m_basepath( path ), m_current( "INBOX" )
{
  // make sure that there is a folder called "Trash"
  string cmd = "mkdir -p " + m_basepath + "/Trash";
  cout << cmd << endl;
  system( cmd.c_str() );

  // obtain list of voicemail messages
  RefreshMessages();

  // obtain list of voicemail folders
  RefreshFolders();
}

// add methods: obtain and set current folder, obtain list of folders

string astVoicebox::GetCurrentFolder()
{
  return m_current;
}

void astVoicebox::SetCurrentFolder( string folder )
{
  m_current = folder;

  RefreshMessages();
}

list<string> astVoicebox::GetFolders( )
{
  return m_folders;
}

void astVoicebox::RefreshFolders()
{
  m_folders.clear();

  // obtain all directories in m_path
  struct direct **files;
  select_path = m_basepath;
  int count = scandir( m_basepath.c_str(), &files, dir_select, alphasort );
  if( count )
    {
      for ( int i=0; i < count; i++ )
	{
	  string folder = string( files[i]->d_name );
	  m_folders.push_back( folder );

	  free( files[i] );
	}
      free( files );
    }
  else
    {
      cout << "no folders found" << endl;
    }  
}

// return name attribute
std::string astVoicebox::GetName()
{
  return m_name;
}

int astVoicebox::GetCount()
{
  return m_messages.size();
}

std::list<astVoiceMessage> astVoicebox::GetMessages()
{
  return m_messages;
}

void astVoicebox::RefreshMessages()
{
  m_messages.clear();

  // obtain all directories in m_path/INBOX
  string path = m_basepath + "/" + m_current;
  cout << "Looking in " << path << endl;

  // chdir( path.c_str() ); // needed?  
  
  struct direct **files;
  int count = scandir( path.c_str(), &files, msg_select, alphasort );
  if( count )
    {
      for ( int i=0; i < count; i++ )
	{
	  m_messages.push_back( astVoiceMessage( m_basepath, m_current, string( files[i]->d_name ) ) );

	  free( files[i] );
	}
      free( files );
    }
  cout << "Refreshing messages done" << endl;
}

// get grouped messages
map<string, list<astVoiceMessage> > 
astVoicebox::GetMessageGroups( int groupmode )
{
  map<string, list<astVoiceMessage> > groups;

  // iterate m_messages
  for( list<astVoiceMessage>::iterator iter = m_messages.begin();
       iter != m_messages.end(); iter ++ )
    {
      string key;
      switch( groupmode )
	{
	case GM_CALLER:
	  {
	    key = (*iter).GetCallerId();
	  } break;
	case GM_DATE:
	  {
	    // TODO
	    key = (*iter).GetTime();
	  } break;
	case GM_NONE: 
	default:
	  {
	    key = "Content";
	  } break;
	}

      (groups[ key ]).push_back( *iter );
    }
  return groups;
}

void astVoicebox::PurgeMessages()
{
  string cmd = "rm " + m_basepath + "/Trash/*";
  cout << cmd << endl;
  system( cmd.c_str() );
}

// is a name needed?
astVoiceMessage::astVoiceMessage( std::string path, std::string folder, std::string name )
  : m_path( path), m_folder( folder ), m_name( name )
{
  Read();
}

int astVoiceMessage::EqualPosition( string s )
{
  return s.find( "=", 0 );

  /*
  int pos = -1;
  int len = s.size();

  for( int i=0; i < len; i ++ )
    {
      if( s[i] == '=' )
	{
	  pos = i;
	  break;
	}
    }
  return pos;
  */
}

void astVoiceMessage::CreateCaller( string v )
{
  // Find first and second quote
  int start = v.find( "\"", 0 );
  int end   = v.find( "\"", start+1 );

  m_caller = v.substr( start, end );
}

void astVoiceMessage::CreateTime( string v )
{
  // think of v as time in unix format and return string in proper format
  char buffer[32], buffer2[32];
  struct tm loctime;

  long t = atol( v.c_str() );

  localtime_r( &t, &loctime );
  strftime( buffer,  32, "%a %d.%m %H:%M", &loctime );
  strftime( buffer2, 32, "%a %d.%m", &loctime );
  
  m_time = string(  buffer );
  m_date = string( buffer2 );
}

void astVoiceMessage::Read()
{
  string path = m_path + "/" + m_folder + "/" + m_name;
  cout << "Reading info about " << path << endl << flush;

  // read text file path (*.txt) and parse relevant information
  ifstream infile;
  infile.open( path.c_str() );
  
  // for each line in path
  string line;
  
  while( !infile.eof() ) 
    {
      getline( infile, line );
      
      // check patterns NAME=VALUE in line
      int len = line.size();

      int eql_pos = EqualPosition( line );
      if( eql_pos > 0 )
	{
	  // before: keyword
	  string tag = line.substr( 0, eql_pos );

	  // after: value
	  string val = line.substr( eql_pos+1, len );

	  if( tag == "origtime" )
	    {
	      CreateTime( val );
	    }
	  else if( tag == "callerid" )
	    {
	      CreateCaller( val );
	    }
	  else if( tag == "duration" )
	    {
	      m_duration = val;
	    }
	  else if( tag == "exten" )
	    {
	      m_extension = val;
	    }
	}
    }
  
  infile.close();
}


void astVoiceMessage::MoveTo( std::string newfolder )
{
  string path = m_path + "/" + m_folder + "/" + m_name;
  unsigned dotpos = path.rfind( ".", path.size() );

  string prefix = path.substr( 0, dotpos );

  string source = prefix + ".*";
  string target = m_path + "/" + newfolder;
    
  string cmd = "mv " + source + " " + target;
  
  cout << cmd << endl;
  system( cmd.c_str() );
}

std::string astVoiceMessage::GetSoundfile( string extension )
{
  // mangle the filename depending on settings
  string path = m_path + "/" + m_folder + "/" + m_name;
  unsigned dotpos = path.rfind( ".", path.size() );

  string prefix = path.substr( 0, dotpos );
  return prefix + "." + extension;
}

// who called
std::string astVoiceMessage::GetCallerId()
{
  return m_caller;
}

std::string astVoiceMessage::GetDuration()
{
  return m_duration;
}

std::string astVoiceMessage::GetTime()
{
  return m_time;
}

std::string astVoiceMessage::GetDate()
{
  return m_date;
}

// the extension dialed
std::string astVoiceMessage::GetExtension()
{
  return m_extension;
}

