/*
    Copyright (c) 2014 - 2026 Christian Schoenebeck

    This file is part of "gigedit" and released under the terms of the
    GNU General Public License version 2.
*/

#include "ReferencesView.h"
#include "global.h"
#include "compat.h"

Glib::ustring gig_to_utf8(const gig::String& gig_string);
std::string note_str(uint8_t note);

ReferencesView::ReferencesView(Gtk::Window& parent) :
    ManagedDialog("", parent, true), m_sample(NULL),
#if HAS_GTKMM_STOCK
    m_closeButton(Gtk::Stock::CLOSE),
#else
    m_closeButton(_("_Close"), true),
#endif
    m_descriptionLabel()
{
    set_title("Nothing selected");

#if !HAS_GTKMM_STOCK
    m_closeButton.set_icon_name("window-close");
#endif

    m_scrolledWindow.add(m_treeView);
    m_scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

#if USE_GTKMM_BOX
    get_content_area()->pack_start(m_descriptionLabel, Gtk::PACK_SHRINK);
    get_content_area()->pack_start(m_scrolledWindow);
    get_content_area()->pack_start(m_summaryLabel, Gtk::PACK_SHRINK);
    get_content_area()->pack_start(m_buttonBox, Gtk::PACK_SHRINK);
#else
    get_vbox()->pack_start(m_descriptionLabel, Gtk::PACK_SHRINK);
    get_vbox()->pack_start(m_scrolledWindow);
    get_vbox()->pack_start(m_summaryLabel, Gtk::PACK_SHRINK);
    get_vbox()->pack_start(m_buttonBox, Gtk::PACK_SHRINK);
#endif

#if GTKMM_MAJOR_VERSION >= 3
    m_descriptionLabel.set_line_wrap();
#endif
    m_descriptionLabel.set_text(_(
        "Selected sample is referenced by the following instruments and their "
        "respective regions. Click on a reference below to jump directly to "
        "its specific dimension region."
    ));

    m_refTreeModel = RefsTreeStore::create(m_columns);
    m_treeView.set_model(m_refTreeModel);
    m_treeView.set_tooltip_text(_(
        "Number of times the selected sample in question is referenced. Click "
        "to jump to the specific reference."
    ));
    m_treeView.append_column(_("Name"), m_columns.m_col_name);
    m_treeView.append_column(_("References"), m_columns.m_col_refcount);
    m_treeView.set_headers_visible(true);
    m_treeView.get_selection()->set_mode(Gtk::SELECTION_SINGLE);
    m_treeView.signal_row_activated().connect(
        sigc::mem_fun(*this, &ReferencesView::onSelectionChanged)
    );

    m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24)
    m_buttonBox.set_margin(5);
#else
    m_buttonBox.set_border_width(5);
#endif
    m_buttonBox.pack_start(m_closeButton, Gtk::PACK_SHRINK);

    m_closeButton.signal_clicked().connect(
        sigc::mem_fun(*this, &ReferencesView::hide)
    );

#if HAS_GTKMM_SHOW_ALL_CHILDREN
    show_all_children();
#endif
}

void ReferencesView::setSample(gig::Sample* sample) {
    m_refTreeModel->clear();

    m_sample = sample;
    if (!sample) {
        set_title("Nothing selected");
        m_summaryLabel.set_text("");
        return;
    }

    set_title(_("References of Sample \"") + sample->pInfo->Name + "\"");

    int filesRefCount = 0;

    gig::File* gig = (gig::File*) sample->GetParent();

    for (size_t i = 0; gig::Instrument* instrument = gig->GetInstrument(i); ++i)
    {
        Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append();
        Gtk::TreeModel::Row rowInstr = *iterInstr;
        rowInstr[m_columns.m_col_name]   = gig_to_utf8(instrument->pInfo->Name);
        rowInstr[m_columns.m_col_instr]  = instrument;
        rowInstr[m_columns.m_col_region] = NULL;

        int instrumentsRefcount = 0;
        for (size_t r = 0; gig::Region* rgn = instrument->GetRegionAt(r); ++r)
        {
            int regionsRefCount = 0;

            for (int dr = 0; dr < 256; ++dr) {
                if (!rgn->pDimensionRegions[dr]) continue;
                if (rgn->pDimensionRegions[dr]->pSample != sample) continue;
                regionsRefCount++;
            }
            if (!regionsRefCount) continue;

            instrumentsRefcount += regionsRefCount;

            Gtk::TreeModel::iterator iterRegion = m_refTreeModel->append(rowInstr.children());
            Gtk::TreeModel::Row rowRegion = *iterRegion;
            rowRegion[m_columns.m_col_name] =
                _("Region from ") + note_str(rgn->KeyRange.low) + _(" to ") + note_str(rgn->KeyRange.high);
            rowRegion[m_columns.m_col_instr]  = NULL;
            rowRegion[m_columns.m_col_region] = rgn;
            rowRegion[m_columns.m_col_refcount] =
                ToString(regionsRefCount) + " " + _("Refs.");
        }

        if (!instrumentsRefcount) {
            m_refTreeModel->erase(iterInstr);
            continue;
        }

        rowInstr[m_columns.m_col_refcount] =
            ToString(instrumentsRefcount) + " " + _("Refs.");
        
        filesRefCount += instrumentsRefcount;
    }

    if (filesRefCount)
        m_summaryLabel.set_text(_("Total References: ") + ToString(filesRefCount));
    else
        m_summaryLabel.set_text(_("This sample is not referenced at all."));

    // unfold all instruments by default
    m_treeView.expand_all();
}

void ReferencesView::onSelectionChanged(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) {
    if (!m_sample) return;

    Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(path);
    if (!it) return;

    Gtk::TreeModel::Row row = *it;

    gig::Instrument* pInstrument = row[m_columns.m_col_instr];
    gig::Region* pRegion = row[m_columns.m_col_region];
    gig::DimensionRegion* pDimRgn = NULL;
    if (pRegion) {
        // pick the 1st dimension region of that region referencing the sample
        for (int dr = 0; dr < pRegion->DimensionRegions && pRegion->pDimensionRegions[dr]; ++dr) {
            if (pRegion->pDimensionRegions[dr]->pSample == m_sample) {
                pDimRgn = pRegion->pDimensionRegions[dr];
                break;
            }
        }
    } else if (pInstrument) {
        // pick the 1st region and its 1st dimension region referencing the sample
        for (size_t r = 0; gig::Region* pRegion = pInstrument->GetRegionAt(r); ++r) {
            for (int dr = 0; dr < pRegion->DimensionRegions && pRegion->pDimensionRegions[dr]; ++dr) {
                if (pRegion->pDimensionRegions[dr]->pSample == m_sample) {
                    pDimRgn = pRegion->pDimensionRegions[dr];
                    break;
                }
            }
        }
    } else {
        return; // no dimension region resolved to be selected, so do nothing
    }

    if (pDimRgn) {
        bool selectionSuccess = dimension_region_selected.emit(pDimRgn);
        if (selectionSuccess) hide();
    }
}
