//---------------------------------------------------------------------------------------
// This file is part of the Lomse library.
// Copyright (c) 2010-present, Lomse Developers
//
// Licensed under the MIT license.
//
// See LICENSE and NOTICE.md files in the root directory of this source tree.
//---------------------------------------------------------------------------------------

#include <UnitTest++.h>
#include <sstream>
#include "lomse_build_options.h"

//classes related to these tests
#include "lomse_injectors.h"
#include "lomse_internal_model.h"
#include "lomse_im_note.h"
#include "lomse_tuplet_engraver.h"
#include "lomse_note_engraver.h"
#include "lomse_rest_engraver.h"
#include "private/lomse_document_p.h"
#include "lomse_gm_basic.h"
#include "lomse_shape_note.h"
#include "lomse_shape_tuplet.h"
#include "lomse_score_meter.h"
#include "lomse_engravers_map.h"
#include "lomse_ldp_analyser.h"
#include "lomse_im_factory.h"

using namespace UnitTest;
using namespace std;
using namespace lomse;


//---------------------------------------------------------------------------------------
class TupletEngraverTestFixture
{
public:
    LibraryScope    m_libraryScope;
    ImoScore*       m_pScore;
    ScoreMeter*     m_pMeter;
    EngraversMap*  m_pStorage;
    NoteEngraver*   m_pNoteEngrv;
    RestEngraver*   m_pRestEngrv;
    TupletEngraver* m_pTupletEngrv;
    GmoShapeTuplet*   m_pTupletShape;
    std::vector<GmoShape*> m_shapes;

    TupletEngraverTestFixture()     //SetUp fixture
        : m_libraryScope(cout)
        , m_pScore(nullptr)
        , m_pMeter(nullptr)
        , m_pStorage(nullptr)
        , m_pNoteEngrv(nullptr)
        , m_pRestEngrv(nullptr)
        , m_pTupletEngrv(nullptr)
        , m_pTupletShape(nullptr)
    {
        m_libraryScope.set_default_fonts_path(TESTLIB_FONTS_PATH);
    }

    ~TupletEngraverTestFixture()    //TearDown fixture
    {
    }

    ImoTuplet* create_tuplet(Document* doc, const string& src)
    {
        string ldp = "(score (vers 2.0)(instrument (musicData (clef G)";
        ldp += src;
        ldp += ")))";

        doc->from_string(ldp);
        m_pScore = static_cast<ImoScore*>( doc->get_im_root()->get_content_item(0) );
        ImoInstrument* pInstr = m_pScore->get_instrument(0);
        ImoMusicData* pMD = pInstr->get_musicdata();
        ImoNote* pNote1 = static_cast<ImoNote*>( pMD->get_child(1) );
        return dynamic_cast<ImoTuplet*>( pNote1->get_relation(0) );
    }

    void prepare_to_engrave_tuplet(ImoTuplet* pTuplet)
    {
        int iInstr = 0;
        int iStaff = 0;
        int iCol = 0;

        list< pair<ImoStaffObj*, ImoRelDataObj*> >& notes = pTuplet->get_related_objects();
        int numNotes = int( notes.size() );

        m_shapes.reserve(numNotes);
        m_pMeter = LOMSE_NEW ScoreMeter(m_pScore, 1, 1, LOMSE_STAFF_LINE_SPACING);
        m_pStorage = LOMSE_NEW EngraversMap();

        //engrave notes/rests
        list< pair<ImoStaffObj*, ImoRelDataObj*> >::iterator it;

        m_pNoteEngrv = LOMSE_NEW NoteEngraver(m_libraryScope, m_pMeter, m_pStorage, 0, 0);
        m_pRestEngrv = LOMSE_NEW RestEngraver(m_libraryScope, m_pMeter, m_pStorage, 0, 0, 0, 0);
        for (it = notes.begin(); it != notes.end(); ++it)
        {
            ImoNoteRest* pNR = static_cast<ImoNoteRest*>( (*it).first );
            GmoShape* pShape;
            if (pNR->is_note())
            {
                ImoNote* pNote = static_cast<ImoNote*>(pNR);
                pShape = m_pNoteEngrv->create_shape(pNote, k_clef_G2, 0,
                                                    UPoint(10.0f, 15.0f) );
            }
            else
            {
                ImoRest* pRest = static_cast<ImoRest*>(pNR);
                pShape = m_pRestEngrv->create_shape(pRest, UPoint(10.0f, 15.0f));
            }
            m_shapes.push_back(pShape);
        }

        //send note data to tuplet engraver
        int i=0;
        for (it = notes.begin(); it != notes.end(); ++it, ++i)
        {
            ImoNoteRest* pNR = static_cast<ImoNoteRest*>( (*it).first );
            AuxObjContext aoc(pNR, m_shapes[i], iInstr, iStaff, iCol, 0, nullptr, -1);
            if (i == 0)
            {
                //first note
                m_pTupletEngrv = LOMSE_NEW TupletEngraver(m_libraryScope, m_pMeter);
                m_pTupletEngrv->set_start_staffobj(pTuplet, aoc);
                m_pStorage->save_engraver(m_pTupletEngrv, pTuplet);
            }
            else if (i == numNotes-1)
            {
                //last note
                m_pTupletEngrv->set_end_staffobj(pTuplet, aoc);
            }
            else
            {
                //intermediate note
                m_pTupletEngrv->set_middle_staffobj(pTuplet, aoc);
            }
        }
    }

    void delete_test_data()
    {
        delete m_pMeter;
        delete m_pStorage;
        delete m_pNoteEngrv;
        delete m_pRestEngrv;
        delete m_pTupletEngrv;
        delete m_pTupletShape;

        std::vector<GmoShape*>::iterator it;
        for (it = m_shapes.begin(); it != m_shapes.end(); ++it)
            delete *it;
        m_shapes.clear();

        m_pMeter = nullptr;
        m_pStorage = nullptr;
        m_pNoteEngrv = nullptr;
        m_pRestEngrv = nullptr;
        m_pTupletEngrv = nullptr;
        m_pTupletShape = nullptr;
    }



};

SUITE(TupletEngraverTest)
{

    TEST_FIXTURE(TupletEngraverTestFixture, CreateTuplet)
    {
        Document doc(m_libraryScope);
        ImoTuplet* pTuplet = create_tuplet(&doc, "(n c4 e (t + 2 3))(n f4 e (t -))");
        CHECK( pTuplet != nullptr );
        CHECK( pTuplet && pTuplet->get_actual_number() == 2 );
        CHECK( pTuplet && pTuplet->get_normal_number() == 3 );
    }

    TEST_FIXTURE(TupletEngraverTestFixture, FeedEngraver)
    {
        Document doc(m_libraryScope);
        ImoTuplet* pTuplet = create_tuplet(&doc, "(n c4 e (t + 2 3))(n f4 e (t -))");
        prepare_to_engrave_tuplet(pTuplet);

        TupletEngraver* pEngrv = dynamic_cast<TupletEngraver*>(m_pStorage->get_engraver(pTuplet));

        CHECK( pEngrv != nullptr );
        CHECK( pEngrv && pEngrv == m_pTupletEngrv );

        delete_test_data();
    }

    TEST_FIXTURE(TupletEngraverTestFixture, CreateTupletShape)
    {
        Document doc(m_libraryScope);
        ImoTuplet* pTuplet = create_tuplet(&doc, "(n c4 e (t + 2 3))(n f4 e (t -))");
        prepare_to_engrave_tuplet(pTuplet);

        RelObjEngravingContext ctx;
        m_pTupletShape = dynamic_cast<GmoShapeTuplet*>( m_pTupletEngrv->create_last_shape(ctx) );
        CHECK( m_pTupletShape != nullptr );

        delete_test_data();
    }

    // decide_on_stems_direction --------------------------------------------------------


}


