// default c++ headers
#include <cstdio>
#include <cstring>
#include <iostream>
#include <fstream>
#include <array>
#include "scanner.hh"
#include "config.hh"
#include "common.hh"
#include "hathor_mysql.hh"
#include "3rdparty/crypt_blowfish.h"

namespace fs = boost::filesystem;
using namespace std;

static FileType fileType( std::string extension ) {

  for( unsigned int i = 1; i < s_fileTypeStrings.size(); i++ ) {
    if( extension.compare(s_fileTypeStrings.at( i )) == 0  )
      return FileType( i );
    }
  return FileType( Unknown );

}
/*
 * hathor_handle_file( std::string file )
 * Process file information and send it to MySQL
 */
static s_fileFields handle_file( std::string file ) {

  s_fileFields filefields;                                    // Structure to store file information for MySQL
  fs::path current = fs::system_complete(file);               // Get current file object
  std::string current_path = current.parent_path().native();  // Get current path minus the file
  TagLib::FileRef f( file.c_str() );                          // Create TagLib object

  if(!f.isNull() && f.tag()) {

    filefields.artist   = ( f.tag()->artist().isEmpty()   ? "Unknown"        : f.tag()->artist().toCString(true));
    filefields.album    = ( f.tag()->album().isEmpty()    ? "Unknown"        : f.tag()->album().toCString(true));
    filefields.title    = ( f.tag()->title().isEmpty()    ? file.c_str()     : f.tag()->title().toCString(true) );
    filefields.genre    = ( f.tag()->genre().isEmpty()    ? "null"           : f.tag()->genre().toCString(true));
    filefields.comment  = ( f.tag()->comment().isEmpty()  ? "null"           : f.tag()->comment().toCString(true));
    filefields.tracknum = ( f.tag()->track()              ? f.tag()->track() : 0 );
    filefields.year     = ( f.tag()->year()               ? f.tag()->year()  : 0 );

    TagLib::AudioProperties *p = f.audioProperties();
    filefields.bitrate  = ( p->bitrate()    ? p->bitrate()    : 0 );
    filefields.sample   = ( p->sampleRate() ? p->sampleRate() : 0 );
    filefields.channels = ( p->channels()   ? p->channels()   : 0 );

    if( p->length() ) {
      filefields.length   = p->length();
      filefields.seconds  = p->length() % 60;
      filefields.minutes  = ( p->length() - filefields.seconds ) / 60;
    } else {
      filefields.length   = 0;
      filefields.seconds  = 0;
      filefields.minutes  = 0;
    }

    TagLib::PropertyMap tags = f.file()->properties();
    for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
      for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
        if(i->first == "ALBUMARTIST") { filefields.albumartist = (j->isEmpty() ? "null" : j->toCString(true)); }
        if(i->first == "LYRICS") { filefields.lyrics = (j->isEmpty() ? "null" : j->toCString(true)); }
        if(i->first == "LABEL") { filefields.label = (j->isEmpty() ? "null" : j->toCString(true));}
      }
    }

    ++file_count;

  }
  else {
    FILE * notagslog;
    notagslog = fopen("no_media_info.log", "a");
    fprintf (notagslog, "Skipping %s, no media information found ...\n",file.c_str());
    printf("Skipping %s, no media information found ...\n", file.c_str());
    //notagslog << file << endl;
    ++no_info_count;
    fclose(notagslog);
  }
  return filefields;
}


json j = parse_file("config.json");
scanner_config s_opts = hathor_config(j);

static int handle_directory( std::string path ) {

  sql::Connection *conn = get_connection(
    s_opts.mysql.user.c_str(),
    s_opts.mysql.pass.c_str(),
    s_opts.mysql.host.c_str(),
    s_opts.mysql.port
  );
  conn->setAutoCommit(0); // Turn autocommit off for transactions.
  conn->setSchema(s_opts.mysql.db);
  unsigned int maxPstmt = get_max_statements(conn);
  sql::PreparedStatement *artist = conn->prepareStatement("INSERT IGNORE INTO artists (arid,name) VALUES(MD5(?),?)");
  sql::PreparedStatement *album = conn->prepareStatement("INSERT IGNORE INTO albums (aid,name,arid,albumartist,label,released) VALUES(MD5(?),?,MD5(?),?,?,?)");
  sql::PreparedStatement *track = conn->prepareStatement("INSERT IGNORE INTO tracks (tid,uri,arid,aid,title,track,genre,lyrics,comment,bitrate,sample,channels,length,minutes,seconds) VALUES(MD5(?),?,MD5(?),MD5(?),?,?,?,?,?,?,?,?,?,?,?)");

  ofstream skipped; // Open file for adding skipped files
  skipped.open("skipped_files.log", ios::out | ios::app); // open log
  ofstream error_log;
  error_log.open("errors.log",ios::out|ios::app);

  fs::path p( fs::system_complete( path.c_str() ) ); // Get the current path we are working on
  cout << p.native() << endl;
  if ( !fs::exists(p) ) { // exit if path was not found
    std::cout << "\nPath Not found: " << p << std::endl;
    return 1;
  }

  fs::recursive_directory_iterator end_iter;

  for (fs::recursive_directory_iterator dir_itr(p); dir_itr != end_iter; ++dir_itr) {
    try {
      if ( fs::is_directory( dir_itr->status() ) && !is_hidden( dir_itr->path() ) ) {
        ++dir_count;
        printf("  * Scanning directory %s ...\n", dir_itr->path().native().c_str());
        continue;
      }
      else if ( fs::is_regular_file( dir_itr->status() ) && !is_hidden( dir_itr->path() ) ) {
        std::string extension = dir_itr->path().extension().native();
        if( !extension.empty() ) {
          extension.erase(extension.find('.'), extension.find('.')+1);
          toLower(extension);
          if( fileType(extension) == FileType( Unknown ) ) {
            std::cout << "  * Skipping file " << dir_itr->path().native() << " ... \n";
            skipped << "[" << extension << "] " << dir_itr->path().native() << "\n";
            ++skip_count;
          } else {
            s_fileFields filefields = handle_file( dir_itr->path().native() );
              
            
            char aid[1000];
            std::string ayear = boost::lexical_cast<std::string>(filefields.year);
            
            strcpy(aid,filefields.album.c_str());
            strcat(aid,filefields.artist.c_str());
            strcat(aid, ayear.c_str());
            // Artist
            artist->setString(1, filefields.artist ); // artists.arid
            artist->setString(2, filefields.artist ); // artists.name
            artist->executeUpdate();
            // Album
            album->setString(1, aid       );  // albums.aid
            album->setString(2, filefields.album       );  // albums.name
            album->setString(3, filefields.artist      );  // albums.arid
            album->setString(4, filefields.albumartist );  // albums.albumartist
            album->setString(5, filefields.label       );  // albums.label
            album->setInt   (6, filefields.year        );  // albums.released
            album->executeUpdate(); 
            // Tracks
            track->setString(1,  dir_itr->path().native() );  // tracks.tid
            track->setString(2,  dir_itr->path().native() );  // tracks.uri
            track->setString(3,  filefields.artist        );  // tracks.arid
            track->setString(4,  aid                      );  // tracks.aid
            track->setString(5,  filefields.title         );  // tracks.title
            track->setInt   (6,  filefields.tracknum      );  // tracks.track
            track->setString(7,  filefields.genre         );  // tracks.genre
            track->setString(8,  filefields.lyrics        );  // tracks.lyrics
            track->setString(9,  filefields.comment       );  // tracks.comment
            track->setInt   (10, filefields.bitrate       );  // tracks.bitrate
            track->setInt   (11, filefields.sample        );  // tracks.sample
            track->setInt   (12, filefields.channels      );  // tracks.channels
            track->setInt   (13, filefields.length        );  // tracks.length
            track->setInt   (14, filefields.minutes       );  // tracks.minutes
            track->setInt   (15, filefields.seconds       );  // tracks.seconds
            track->executeUpdate();
         
         
          }
        } else {
          //std::cout << "  * Skipping file " << dir_itr->path().native() << " ... \n";
          skipped << "[no extension] " << dir_itr->path().native() << "\n";
          ++skip_count;
        }
      }
      else {
        skipped << "[other] " << dir_itr->path().native() << "\n";
        ++other_count;
      }
    } catch (const sql::SQLException & sqlError ) {
      error_log << "[SQLError] : " << dir_itr->path().filename() << " " << sqlError.what() << std::endl;
      continue;
    }
    catch (const std::exception & ex) {
      ++err_count;
      std::cout << dir_itr->path().filename() << " " << ex.what() << std::endl;
      error_log << dir_itr->path().filename() << " " << ex.what() << std::endl;
    }
  }

  skipped.close();
  error_log.close();
  conn->commit();


  delete artist;
  delete album;
  delete track;
  delete conn;
  return 0;

}

int main( int argc, char *argv[] ) {

  if(argc > 1) s_opts.scanner.path = fs::current_path().native();
  handle_directory(s_opts.scanner.path);
  std::cout << "Directorys  : " << dir_count << endl;
  std::cout << "Media       : " << file_count << endl;
  std::cout << "No tags     : " << no_info_count << endl;
  std::cout << "Skipped     : " << skip_count << endl;
  std::cout << "Other Files : " << other_count << endl;
  std::cout << "Errors      : " << err_count << endl;

  return 0;
}
