/*
 * $Id: bed_autosql.cpp 667520 2023-05-16 16:14:25Z foleyjp $
 *
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Authors:  Frank Ludwig
 *
 */

#include <ncbi_pch.hpp>
#include <corelib/ncbistr.hpp>
#include <objects/seqloc/Seq_interval.hpp>
#include <objects/general/Object_id.hpp>
#include <objects/general/User_object.hpp>
#include <objtools/readers/read_util.hpp>
#include <objtools/readers/reader_message.hpp>
#include <objtools/readers/message_listener.hpp>
#include <objtools/readers/reader_error_codes.hpp>
#include "bed_autosql.hpp"

#include <functional>

BEGIN_NCBI_SCOPE
BEGIN_SCOPE(objects);

//  ============================================================================
CBedAutoSql::CBedAutoSql(int bedFlags):
    mBedFlags(bedFlags),
    mColumnCount(0)
//  ============================================================================
{
}

//  ============================================================================
CBedAutoSql::~CBedAutoSql()
//  ============================================================================
{
}

//  ============================================================================
bool
CBedAutoSql::xParseAutoSqlColumnDef(
    const string& line,
    string& format,
    string& name,
    string& description)
//  ============================================================================
{
    string tail;
    NStr::SplitInTwo(line, " \t", format, tail, NStr::fSplit_MergeDelimiters);
    NStr::SplitInTwo(tail, " \t", name, description, NStr::fSplit_MergeDelimiters);
    NStr::ToLower(format);
    name = NStr::Replace(name, ";", "");
    description = NStr::Replace(description, "\"", "");
    return true;
}


void 
CBedAutoSql::xProcessLine(
        const string& line,
        bool& readingTable,
        size_t& autoSqlColCounter)
{
    if (NStr::IsBlank(line)) {
        return;
    }

    if (readingTable) {
        if (NStr::StartsWith(line, ")")) {
            readingTable = false;
            if (line.size()>1) {
                xProcessLine(NStr::TruncateSpaces(line.substr(1)), 
                    readingTable,
                    autoSqlColCounter);    
                    return;
            }
            return;
        }
        string format, name, description;
        xParseAutoSqlColumnDef(line, format, name, description);
        if (!mWellKnownFields.ProcessTableRow(autoSqlColCounter, name, format)) {
            mCustomFields.Append(
                CAutoSqlCustomField(
                autoSqlColCounter, format, name, description));
        }
        ++autoSqlColCounter;
        return;
    }
            
    if (NStr::StartsWith(line, "(")) {
        readingTable = true;
        if (line.size()>1) {
            xProcessLine(NStr::TruncateSpaces(line.substr(1)), 
                readingTable,
                autoSqlColCounter);    
            return;
        }
        return;
    }
            
    if (line == "as") {
        return;
    }
    
    string key, value;
    NStr::SplitInTwo(line, ":", key, value, NStr::fSplit_MergeDelimiters);
    NStr::TruncateSpacesInPlace(key);
    NStr::TruncateSpacesInPlace(value);
    if (key == "fieldCount") {
        mColumnCount = NStr::StringToUInt(value);
    }
    mParameters[key] = value;
}


//  ============================================================================
bool
CBedAutoSql::Load(
    CNcbiIstream& istr,
    CReaderMessageHandler& messageHandler)
//  =============================================================================
{
    bool readingTable = false;
    size_t autoSqlColCounter(0);

    while (!istr.eof()) {
        string line = xReadLine(istr);
        xProcessLine(line, readingTable, autoSqlColCounter);
    }

    if (mColumnCount == 0) {
        mColumnCount = mWellKnownFields.NumFields() + mCustomFields.NumFields();
    }
    //Dump(cerr);
    return Validate(messageHandler);
}

//  =============================================================================
size_t
CBedAutoSql::ColumnCount() const
//  =============================================================================
{
    return mColumnCount;
}

//  ===============================================================================
string
CBedAutoSql::xReadLine(
    CNcbiIstream& istr)
//  ===============================================================================
{
    string line;
    while (line.empty()  &&  istr.good()) {
        std::getline(istr, line);
        NStr::TruncateSpacesInPlace(line);
    }
    return line;
}

//  ===============================================================================
bool
CBedAutoSql::ReadSeqFeat(
    const CBedColumnData& columnData,
    CSeq_feat& feat,
    CReaderMessageHandler& messageHandler) const
//  ===============================================================================
{
    //rules:
    // true: useful data was generated, even if incomplete
    // false: any data will be flawed, don't use
    // exception: something so bad we can't deal with it on this level.
    bool success =
        mWellKnownFields.SetLocation(
            columnData, mBedFlags, feat, messageHandler)  &&
        mWellKnownFields.SetTitle(
            columnData, feat, messageHandler) && 
        mWellKnownFields.SetRegion(
            columnData, feat, messageHandler) &&
        mWellKnownFields.SetDisplayData(
            columnData, mBedFlags, feat, messageHandler) &&
        mCustomFields.SetUserObject(
            columnData, mBedFlags, feat, messageHandler);
    if (!success) {
        return false;
    }
    return true;
}

//  ===============================================================================
bool
CBedAutoSql::Validate(
    CReaderMessageHandler& messageHandler) const
//  ===============================================================================
{
    if ( !mWellKnownFields.Validate(messageHandler) ||
            !mCustomFields.Validate(messageHandler)) {
        return false;
    }
    if (mColumnCount != mWellKnownFields.NumFields() + mCustomFields.NumFields()) {
        CReaderMessage fatal(
            EDiagSev::eDiag_Error,
            0,
            "AutoSql: The declared column count differs from the actual column count");
        messageHandler.Report(fatal);
        return false;
    }
    return true;
}

END_SCOPE(objects)
END_NCBI_SCOPE
