/*
 *  This file is part of FSA, a sequence alignment algorithm.
 *  Source code in this file was written by Jaeyoung Do.
 */

#ifndef DB_POSTGRES_INCLUDED
#define DB_POSTGRES_INCLUDED

#include <queue>
#include <string>

#include "libpq-fe.h" 
#include "manager/db_misc.h"

#include "seq/sequence.h"
#include "util/hash_fcn.h"
#include "annealing/SparseMatrix.h"

namespace fsa {

  class Edge;
  class Column;
  class smaller_weight;

  /// Map a position in a sequence to the containing Column*.
  typedef std::vector<Column*> Seq_pos_col_map;

  /// Maximum length of queries
#define DB_MAX_QUERY_LENGTH 1000

  /// DB_postgres class
  /*
   * This class contains database methods depending on PostgreSQL.
   * This class has developed based on PostgreSQL 8.3.1 
   *
   * For more information about PostgreSQL, 
   * please visit http://www.postgresql.org
   *
   * For more information about database tables, schemas, ids, etc, 
   * please see db_adapter.h
   */

  class DB_postgres 
  {
  public:
    /// Constructor
    DB_postgres();

    /// Destructor
    ~DB_postgres();

    /// Attempt to establish a connection to the given database host
    /*
     * With given information, try to make a connection to database server.
     * Note that to establish a connection, at least db_hostname (or db_hostaddr) and db_name
     * must be provided.
     */
    bool connect_db (const char *hostname, const char *hostaddr, const char* dbname, 
		     const int port, const char *user, const char *password); 

    /// Disconnect from the database
    void disconnect_db();

    /// Get methods

    /// Return sequence schema id 
    /* 
     * Looking up the database with a combination of hash_key (that has been generated by using
     * input sequences), the number of the input sequences, and the average length of the sequences.
     */
    int get_seqs_schema_id (const uint32_t hash_key, const int num_seqs, const float avg_length);

    /// Return sequence schema id
    /*
     * Just return the sequence schema id. This method is called only from the WORKER instance.
     */
    int get_seqs_schema_id (); 

    /// Return parameter table id
    /*
     * Looking up the database with a combination of options that has been inputed when running FSA.
     * If no options are used, then select conditions are filled with default options.
     */
    int get_params_table_id (const DB_opts &db_opts);

    /// Return parameter table id  
    /*
     * Just return the parameter table id. This method is called  only from the WORKER instance.
     */
    int get_params_table_id ();

    /// Return the number of jobs
    /*
     * Return the number of workers that have been used to generate the data.
     */
    int get_num_jobs ();

    /// Create sparse matrices
    /* 
     * Construct all sparse matrices with pairwise posterior probabilties in the database.
     */
    int get_sparse_matrices (const int worker_id, const Sequence_database &seq_db_internal, 
			     const std::vector<std::vector<int> > &num_cells, std::vector<std::vector<SparseMatrix*> > &sparse_matrices); 

    /// Create a sparse matrix
    /*
     * Construct a sparse matrix of the sequence pair (seq1, seq2)
     */
    bool get_sparse_matrix (const int worker_id, const int seq1, const int seq2, 
			    const int seq1Length, const int seq2Length, std::vector<std::vector<SparseMatrix*> > &sparse_matrices);

    /// Get a number of cells that is constituted for each sparse matrix
    /*
     * A num_cells table contains the number of data cells that are used to construct each sparse matrix.
     */
    int get_num_cells (const int worker_id, std::vector<std::vector<int> > &num_cells);

    /// Get the list of available pairs
    /*
     * If there's N input sequences, total N*(N-1)/2 pairs are considered for the annealing. However, it does
     * not always heppen (ex, --fast option). Therefore, we need to determine which sequence pairs would be 
     * considered for the sequence annealing.
     */
    int get_list_of_available_pairs (const int worker_id, std::vector<std::vector<int> > &available_sparse_matrices, 
				     const Sequence_database *seq_db_internal, double &avg_sparse_matrix_size, int &num_pairs, int &orig_edges_size);

    /// Get all candidate edges of the null alignment
    /*
     * Construct the initial priority queue with candidate edges of the null alignment. 
     */
    int get_heaps (const int worker_id, double &min_edge_weight, std::vector<Seq_pos_col_map> &seq_pos_col_maps, 
		   std::priority_queue<Edge*, std::vector<Edge*>, smaller_weight> &edges); 

    /// Get some of candidate edges of the null alignment	
    /*
     * When there's a restriction on the memory size that can be consumed, instead of getting all candidate edges
     * at once, it is needed to get some candidate edges. The number of candidate edges that are gotten at once is 
     * size.
     */
    bool get_merged_heap (const int size, const int offset, double &min_edge_weight, 
			  std::vector<Seq_pos_col_map> &seq_pos_col_maps, std::priority_queue<Edge*, std::vector<Edge*>, smaller_weight> &edges, bool &last); 

    /// Return the size of merged heap.
    int get_merged_heap_size(); 

    /// Put pairwise posterior probabilities into the database.
    void flush_sparse_matrix_buffer(const char *string);

    /// Put numbers of data cells of sparse matrices into the database.
    void flush_num_cells_buffer(const char *string);

    /// Put candidate edges of the null alignment into the database.
    void flush_heap_buffer(const char *string);

    /// Put re-weighted edges into the database.
    void flush_merged_heap_buffer(const char* string); 

    /// Insert-related methods

    /// Insert a tuple into the fsa_main table for a schema related to the input sequences.	
    bool insert_fsa_schema (const uint32_t &hash_key, const int &num_seqs, const float &avg_length); 

    /// Insert tuples into the sequence table, with each tuple matching to the each input sequence.
    bool insert_seqs_table (const Sequence_database &seq_db_internal);

    /// Insert a tuple into the parameter table for the options that have been used when generating  the data.
    bool insert_params_table (const DB_opts &db_opts);


    /// Set methods	

    ///	Set sequence schema id.
    void set_seqs_schema_id (const int seqs_schema_id); 

    /// Set parameter table id.
    void set_params_table_id (const int params_table_id);

    /// Set worker id.
    void set_worker_id (const int worker_id); 

    /// Update methods

    /// Update the tuple that has already been in the main_fsa table.
    bool update_fsa_table (); 

    /// Update the tuple that has already been in the parameter table.
    bool update_params_table (const int num_parallelized_jobs); 


    /// Create methods

    /// Create fsa_table which is the main table.
    bool create_fsa_table ();

    /// Create a sequence schema which is related to a set of input sequences.
    bool create_seqs_schema ();

    /// Create the parameter table which belongs to a sequence schema.
    bool create_params_table(); 

    /// Create a sequence table.
    bool create_seqs_table(); 

    /// Create a sparse matrix table with given id.
    bool create_sparse_matrix_table (const int id); 

    /// Create an index on the sparse matrix table_id.
    bool create_sparse_matrix_table_index (const int id);

    /// Create a num_cells table.
    bool create_num_cells_table (const int id); 

    /// Create a heap table.
    bool create_heap_table (const int id);

    /// Create the merged_heap_table.
    bool create_merged_heap_table (); 

    /// Create an index on the merged heap table.
    bool create_merged_heap_table_index (); 


    /// Delete methods 

    /// Delete tuples in the sparse matrix table.
    bool delete_sparse_matrix_table (const int id); 

    /// Delete tuples in the num_cells table.
    bool delete_num_cells_table (const int id); 

    /// Delete tuples in the heap table.
    bool delete_heap_table (const int id); 

    /// Delete tuples in the merged heap table.
    bool delete_merged_heap_table (); 


    /// Drop methods

    /// Drop the sparse matrix table index
    bool drop_sparse_matrix_table_index (const int id); 

    /// Drop the merged heap table index
    bool drop_merged_heap_table_index (); 


    /// Execute_query
    /*
     * Execute the query in m_query 
     */
    bool execute_query ();

    /// move contents in the buffer (copy_string) to the database tables
    /*
     * NOTE: it is PostgreSQL specific methods. It is much much faster than just "INSERT" query
     */	
    bool copy_stdin (const char *table_name, const char* copy_string);

    /// move contents in the database tables to the standard output
    /*
     * NOTE: it is PostgreSQL specific methods. It is much much faster than just "SELECT" query
     */
    bool copy_stdout (const char *table_name); 

    /// Copy heap tables to the merged heap table
    /*
     * The merged heap table is used only when the db-maxram option is used.
     */
    bool copy_to_merged_heap_table (const int id); 

  private:
    PGconn     *m_conn;                                     // the class to access PostgreSQL database
    PGresult   *m_res;                                      // the class to represent the query result tuples
    char        m_query[DB_MAX_QUERY_LENGTH];               // common buffer for queries
    char        m_exec[DB_MAX_QUERY_LENGTH];                // common buffer for execution commands

    int         m_seqs_schema_id;        // seqeunce schema id
    int         m_params_table_id;       // parameter table id
    int 	    m_num_jobs;              // the number of jobs (workers)

    sstring     m_seqs_schema;           // sequence schema name
    sstring     m_params_table;          // parameter table name
    sstring     m_seqs_table;            // sequence table name 

    sstring     m_sparse_matrix_info;   // sparse matrix info
    sstring     m_heap_info;            // heap info

    sstring     m_fsa_table;            // fsa table name
    sstring     m_heap_table;           // heap table name
    sstring     m_merged_heap_table;    // merged heap table name
    sstring     m_sparse_matrix_table;  // sparse matrix table name
    sstring     m_num_cells_table;      // num cells table name

    sstring     m_sparse_matrix_prefix; // sparse matrix prefix
    sstring 	m_heap_prefix;          // heap prefix
    sstring 	m_merged_heap_prefix;   // merged heap prefix
  };

}

#endif
