/*
   Copyright (C) 2008-2023 J.Rios <anonbeat@gmail.com>
   Copyright (C) 2024-2026 Tiago T Barrionuevo <thothix@protonmail.com>

   This file is part of Guayadeque Music Player.

   Guayadeque is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Guayadeque is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Guayadeque. If not, see <https://www.gnu.org/licenses/>.
*/
#include "DbLibrary.h"

#include "AlbumBrowser.h"
#include "EventCommandIds.h"
#include "Config.h"
#include "DynamicPlayList.h"
#include "LibUpdate.h"
#include "MainFrame.h"
#include "MediaViewer.h"
#include "MD5.h"
#include "PlayerPanel.h"
#include "TagInfo.h"
#include "Utils.h"
#include "TreePanel.h"

#include <wx/mstream.h>
#include <wx/wfstream.h>

namespace Guayadeque {

    WX_DEFINE_OBJARRAY(guTrackArray)
    WX_DEFINE_OBJARRAY(guListItems)
    WX_DEFINE_OBJARRAY(guArrayListItems)
    WX_DEFINE_OBJARRAY(guAlbumItems)
    WX_DEFINE_OBJARRAY(guCoverInfos)
    WX_DEFINE_OBJARRAY(guAS_SubmitInfoArray)
    WX_DEFINE_OBJARRAY(guArrayDbLibrary)

    #define GU_CURRENT_DBVERSION    "21"

    // -------------------------------------------------------------------------------- //
    // Various functions
    // -------------------------------------------------------------------------------- //

    wxString GetOrderOnce(wxString &order)
    {
        if (!order.IsEmpty())
        {
            wxString result = ", " + order;
            order = "";
            return result;
        }
        return order;
    }

    // -------------------------------------------------------------------------------- //
    wxString GetSongsSortSQL(const wxArrayInt *aorder, const wxArrayInt *aorderdesc, const bool force_fallback = false)
    {
        wxString sComma = "";
        wxString query = wxT(" ORDER BY ");

        for (size_t i = 0; i < aorder->GetCount(); i++)
        {
            query += sComma;

            switch (aorder->Item(i))
            {
                case guTRACKS_ORDER_TITLE:
                    query += wxT("song_name");
                    break;

                case guTRACKS_ORDER_ARTIST:
                    query += wxT("song_artist");
                    break;

                case guTRACKS_ORDER_ALBUMARTIST:
                    query += wxT("song_albumartist");
                    break;

                case guTRACKS_ORDER_ALBUM:
                    query += wxT("song_album");
                    break;

                case guTRACKS_ORDER_GENRE:
                    query += wxT("song_genre");
                    break;

                case guTRACKS_ORDER_COMPOSER:
                    //query += wxT( "composer_name " );
                    query += wxT("song_composer");
                    break;

                case guTRACKS_ORDER_DISK:
                    query += wxT("song_disk");
                    break;

                case guTRACKS_ORDER_NUMBER:
                    query += wxT("song_number");
                    break;

                case guTRACKS_ORDER_LENGTH:
                    query += wxT("song_length");
                    break;

                case guTRACKS_ORDER_YEAR:
                    query += wxT("song_year");
                    break;

                case guTRACKS_ORDER_RATING:
                    query += wxT("song_rating");
                    break;

                case guTRACKS_ORDER_BITRATE:
                    query += wxT("song_bitrate");
                    break;

                case guTRACKS_ORDER_PLAYCOUNT:
                    query += wxT("song_playcount");
                    break;

                case guTRACKS_ORDER_LASTPLAY:
                    query += wxT("song_lastplay");
                    break;

                case guTRACKS_ORDER_ADDEDDATE:
                    query += wxT("song_addedtime");
                    break;

                case guTRACKS_ORDER_FORMAT:
                    query += wxT("song_format");
                    break;

                case guTRACKS_ORDER_FILEPATH:
                    query += wxT("song_path");
                    break;

                default:
                    ////if (!force_fallback)
                        return wxEmptyString;
                    ////query += wxT("song_artist");
                    ////return query;
            }

            if (aorderdesc->Item(i))
                query += wxT(" DESC");
            if (sComma.IsEmpty())
                sComma = ", ";
        }

        wxString SAI = wxT("song_albumid");
        wxString SAR = aorder->Index(guTRACKS_ORDER_ARTIST) == wxNOT_FOUND ? "song_artist" : "";
        wxString SAL = aorder->Index(guTRACKS_ORDER_ALBUM) == wxNOT_FOUND ? "song_album" : "";
        wxString SD  = aorder->Index(guTRACKS_ORDER_DISK) == wxNOT_FOUND ? "song_disk" : "";
        wxString SN  = aorder->Index(guTRACKS_ORDER_NUMBER) == wxNOT_FOUND ? "song_number" : "";
        wxString SY  = aorder->Index(guTRACKS_ORDER_YEAR) == wxNOT_FOUND ? "song_year" : "";

        for (size_t i = 0; i < aorder->GetCount(); i++)
        {
            switch (aorder->Item(i))
            {
                case guTRACKS_ORDER_DISK:
                    query += GetOrderOnce(SAI) + GetOrderOnce(SN);
                    break;

                case guTRACKS_ORDER_COMPOSER:
                    query += GetOrderOnce(SAR) + GetOrderOnce(SAL) + GetOrderOnce(SD) + GetOrderOnce(SAI) + GetOrderOnce(SN);
                    break;

                case guTRACKS_ORDER_ARTIST:
                case guTRACKS_ORDER_ALBUMARTIST:
                    query += GetOrderOnce(SY) + GetOrderOnce(SAL) + GetOrderOnce(SD) + GetOrderOnce(SAI) + GetOrderOnce(SN);
                    break;

                case guTRACKS_ORDER_ALBUM:
                    query += GetOrderOnce(SD) + GetOrderOnce(SAI) + GetOrderOnce(SN);
                    break;

                case guTRACKS_ORDER_YEAR:
                    query += GetOrderOnce(SAL) + GetOrderOnce(SD) + GetOrderOnce(SAI) + GetOrderOnce(SN);
                    break;

                case guTRACKS_ORDER_ADDEDDATE:
                    query += GetOrderOnce(SAL) + GetOrderOnce(SD) + GetOrderOnce(SAI) + GetOrderOnce(SN);
                    break;

                default:
                    break;
            }
        }

        return query;
    }

    // -------------------------------------------------------------------------------- //
    int guAlbumItemSearch(const guAlbumItems &items, int start, int end, int id)
    {
        if (start >= end && items[start].m_Id != id)
            return -1;

        int MidPos = ((start + end) / 2);

        if (items[MidPos].m_Id == id)
            return MidPos;
        if (id > items[MidPos].m_Id)
            return guAlbumItemSearch(items, MidPos + 1, end, id);

        return guAlbumItemSearch(items, start, MidPos - 1, id);
    }

    // -------------------------------------------------------------------------------- //
    guAlbumItem *guAlbumItemGetItem(guAlbumItems &items, int id)
    {
        int index;
        int count = items.Count();
        if (count)
        {
            index = guAlbumItemSearch(items, 0, count - 1, id);
            if (index != wxNOT_FOUND)
                return &items[index];
            guLogError(wxT("Could not find in cache the albumid : %i"), id);
        }
        return nullptr;
    }

    // -------------------------------------------------------------------------------- //
    wxString guAlbumItemsGetName(const guAlbumItems &items, int id)
    {
        int index;
        int count = items.Count();
        if (count)
        {
            index = guAlbumItemSearch(items, 0, count - 1, id);
            if (index != wxNOT_FOUND)
                return items[index].m_Name;
            guLogError(wxT("Could not find in cache the albumid : %i"), id);
        }
        return wxEmptyString;
    }

    // -------------------------------------------------------------------------------- //
    int guAlbumItemsGetCoverId(const guAlbumItems &items, int id)
    {
        int index;
        int count = items.Count();
        if (count)
        {
            index = guAlbumItemSearch(items, 0, count - 1, id);
            if (index != wxNOT_FOUND)
                return items[index].m_CoverId;
        }
        return 0;
    }

    // -------------------------------------------------------------------------------- //
    int guListItemSearch(const guListItems &items, int start, int end, int id)
    {
        if (start >= end && items[start].m_Id != id)
            return -1;

        int MidPos = ((start + end) / 2);

        if (items[MidPos].m_Id == id)
            return MidPos;
        if (id > items[MidPos].m_Id)
            return guListItemSearch(items, MidPos + 1, end, id);
        return guListItemSearch(items, start, MidPos - 1, id);
    }

    // -------------------------------------------------------------------------------- //
    wxString guListItemsGetName(const guListItems &items, int id)
    {
        int index;
        int count = items.Count();
        if (count)
        {
            index = guListItemSearch(items, 0, count - 1, id);
            if (index != wxNOT_FOUND)
                return items[index].m_Name;
        }
        return wxEmptyString;
    }

    // -------------------------------------------------------------------------------- //
    wxArrayInt GetArraySameItems(const wxArrayInt &Source, const wxArrayInt &Oper)
    {
        wxArrayInt RetVal = Source;
        int count = RetVal.Count();
        if (count)
        {
            for (int index = count - 1; index >= 0; index--)
            {
                if (Oper.Index(RetVal[index]) == wxNOT_FOUND)
                    RetVal.RemoveAt(index);
            }
        }
        return RetVal;
    };

    // -------------------------------------------------------------------------------- //
    wxArrayInt GetArrayDiffItems(const wxArrayInt &Source, const wxArrayInt &Oper)
    {
        wxArrayInt RetVal = Source;
        int count = RetVal.Count();
        if (count)
        {
            for (int index = count - 1; index >= 0; index--)
            {
                if (Oper.Index(RetVal[index]) != wxNOT_FOUND)
                    RetVal.RemoveAt(index);
            }
        }
        return RetVal;
    };

    wxString Contains_Or_Like(const wxString &field, const wxString &filter, const bool collation_enabled)
    {
        if (collation_enabled)
            return wxT("CONTAINS_FA(" + field + ", '") + filter + wxT("')>-1 ");
        return field + wxT(" LIKE '%") + filter + wxT("%' ");
    }

    // -------------------------------------------------------------------------------- //
    wxString DirectoryFilterToSQL(const wxString &DiFilter)
    {
        wxString Filter = escape_query_str(DiFilter);
        wxString RetVal;

        RetVal = wxT("song_path LIKE '") + Filter + wxT("%' ");

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    wxString LabelFilterToSQL(const wxArrayInt &LaFilters)
    {
        int count;
        wxString subquery;
        wxString RetVal = wxEmptyString;
        if ((count = LaFilters.Count()))
        {
            subquery = wxT("(");
            for (int index = 0; index < count; index++)
            {
                subquery += wxString::Format(wxT("%u,"), LaFilters[index]);
            }
            subquery = subquery.RemoveLast(1) + wxT(")");

            RetVal += wxT("( (song_artistid IN ( SELECT settag_artistid FROM settags WHERE "
                "settag_tagid IN ") + subquery + wxT(" and settag_artistid > 0 ) ) OR");
            RetVal += wxT(" (song_albumid IN ( SELECT settag_albumid FROM settags WHERE "
                "settag_tagid IN ") + subquery + wxT(" and settag_albumid > 0 ) ) OR");
            RetVal += wxT(" (song_id IN ( SELECT settag_songid FROM settags WHERE "
                "settag_tagid IN ") + subquery + wxT(" and settag_songid > 0 ) ) )");
        }
        return RetVal;
    }


    // -------------------------------------------------------------------------------- //
    // guTrack
    // -------------------------------------------------------------------------------- //
    bool guTrack::ReadFromFile(const wxString &filename)
    {
        guTagInfo *TagInfo;

        TagInfo = guGetTagInfoHandler(filename);
        if (!TagInfo)
        {
            m_SongName = m_FileName.AfterLast(wxT('/'));
            m_AlbumName = m_FileName.BeforeLast(wxT('/'));
            m_FileSize = guGetFileSize(filename);
            return false;
        }

        m_PathId = wxNOT_FOUND;
        m_Path = GetPathAddTrailSep(wxPathOnly(filename));

        if (TagInfo->Read())
        {
            m_Composer = TagInfo->m_Composer;
            m_ArtistName = TagInfo->m_ArtistName;
            m_AlbumArtist = TagInfo->m_AlbumArtist;
            m_AlbumName = TagInfo->m_AlbumName;
            m_GenreName = TagInfo->m_GenreName;
            m_SongName = TagInfo->m_TrackName;

            m_Number = TagInfo->m_Track;
            m_Year = TagInfo->m_Year;
            m_Length = TagInfo->m_Length;
            m_Bitrate = TagInfo->m_Bitrate;
            m_Comments = TagInfo->m_Comments;
            m_Disk = TagInfo->m_Disk;
        } else
            guLogError(wxT("guTrack.ReadFromFile: Cannot read tags from '%s'"), filename.c_str());

        if (m_ArtistName.IsEmpty())
            m_ArtistName = _("Unknown");
        m_ArtistId = wxNOT_FOUND;

        m_AlbumArtistId = wxNOT_FOUND;;

        if (m_AlbumName.IsEmpty() && !m_Path.IsEmpty())
            m_AlbumName = m_Path.BeforeLast(wxT('/')).AfterLast(wxT('/'));
        m_AlbumId = wxNOT_FOUND;;

        m_CoverId = 0;

        if (m_GenreName.IsEmpty())
            m_GenreName = _("Unknown");
        m_GenreId = wxNOT_FOUND;;

        m_FileName = filename;
        m_FileSize = guGetFileSize(filename);

        if (m_SongName.IsEmpty())
            m_SongName = m_FileName.AfterLast(wxT('/')).BeforeLast(wxT('.'));

        m_SongId = wxNOT_FOUND;
        m_Rating = wxNOT_FOUND;

        delete TagInfo;
        return true;
    }


    // -------------------------------------------------------------------------------- //
    // guDbLibrary
    // -------------------------------------------------------------------------------- //
    guDbLibrary::guDbLibrary() : guDb()
    {
        m_MediaViewer = nullptr;
        m_NeedUpdate = false;
        m_LastAlbum = wxT("__Gu4y4d3qu3__");
        m_LastGenre = wxT("__Gu4y4d3qu3__");
        m_LastCoverId = wxNOT_FOUND;
        m_LastArtist = wxT("__Gu4y4d3qu3__");
        m_LastArtistId = wxNOT_FOUND;
        m_LastAlbumArtist = wxT("__Gu4y4d3qu3__");
        m_LastAlbumArtistId = wxNOT_FOUND;
        m_LastComposer = wxT("__Gu4y4d3qu3__");
        m_LastComposerId = wxNOT_FOUND;
        m_TracksMultiOrder = {};

        CheckDbVersion();
    }

    // -------------------------------------------------------------------------------- //
    guDbLibrary::guDbLibrary(const wxString &dbname, const wxString &unique_id) : guDb(dbname)
    {
        m_DbUniqueId = unique_id;
        m_MediaViewer = nullptr;
        m_NeedUpdate = false;
        m_LastAlbum = wxT("__Gu4y4d3qu3__");
        m_LastGenre = wxT("__Gu4y4d3qu3__");
        m_LastCoverId = wxNOT_FOUND;
        m_LastArtist = wxT("__Gu4y4d3qu3__");
        m_LastArtistId = wxNOT_FOUND;
        m_LastAlbumArtist = wxT("__Gu4y4d3qu3__");
        m_LastAlbumArtistId = wxNOT_FOUND;
        m_LastComposer = wxT("__Gu4y4d3qu3__");
        m_LastComposerId = wxNOT_FOUND;
        m_TracksMultiOrder = {};

        CheckDbVersion();
    }

    // -------------------------------------------------------------------------------- //
    guDbLibrary::guDbLibrary(guDb *db) : guDb()
    {
        m_MediaViewer = nullptr;
        m_NeedUpdate = false;
        m_LastAlbum = wxT("__Gu4y4d3qu3__");
        m_LastGenre = wxT("__Gu4y4d3qu3__");
        m_LastCoverId = wxNOT_FOUND;
        m_LastArtist = wxT("__Gu4y4d3qu3__");
        m_LastArtistId = wxNOT_FOUND;
        m_LastAlbumArtist = wxT("__Gu4y4d3qu3__");
        m_LastAlbumArtistId = wxNOT_FOUND;
        m_LastComposer = wxT("__Gu4y4d3qu3__");
        m_LastComposerId = wxNOT_FOUND;
        m_TracksMultiOrder = {};

        m_Db = db->GetDb();
    }

    // -------------------------------------------------------------------------------- //
    guDbLibrary::~guDbLibrary()
    {
        Close();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetMediaViewer(guMediaViewer *mediaviewer)
    {
        m_MediaViewer = mediaviewer;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetDbVersion()
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        unsigned long RetVal = 0;

        query = wxT("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'Version';");

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            dbRes.Finalize();

            query = wxT("SELECT version FROM Version;");
            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                RetVal = dbRes.GetInt(0);
        } else
        {
            query = wxT("CREATE TABLE Version( version INTEGER );");
            ExecuteUpdate(query);
        }
        dbRes.Finalize();

        return RetVal;
    }

    /*
    // -------------------------------------------------------------------------------- //
    void guDbLibrary::DoCleanUp( void )
    {
      wxString query;
      wxArrayInt SongsToDel;
      wxArrayInt CoversToDel;
      wxSQLite3ResultSet dbRes;
      wxString FileName;

      guConfig * Config = ( guConfig * ) guConfig::Get();
      wxArrayString LibPaths = Config->ReadAStr( wxT( "LibPath" ), wxEmptyString, wxT( "libpaths" ) );

      CheckSymLinks( LibPaths );

      query = wxT( "SELECT DISTINCT song_id, song_filename, song_path FROM songs " );
      dbRes = ExecuteQuery( query );

      while( dbRes.NextRow() )
      {
        FileName = dbRes.GetString( 2 ) + wxT( "/" ) + dbRes.GetString( 1 );

        if( !wxFileExists( FileName ) || !CheckFileLibPath( LibPaths, FileName ) )
          SongsToDel.Add( dbRes.GetInt( 0 ) );
      }
      dbRes.Finalize();

      query = wxT( "SELECT DISTINCT cover_id, cover_path FROM covers;" );
      dbRes = ExecuteQuery( query );

      while( dbRes.NextRow() )
      {
        if( !wxFileExists( dbRes.GetString( 1 ) ) )
          CoversToDel.Add( dbRes.GetInt( 0 ) );
      }
      dbRes.Finalize();

      CleanItems( SongsToDel, CoversToDel );
    }
    */

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::CleanFiles(const wxArrayString &Files)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxArrayInt Tracks;
        wxArrayInt Covers;
        int Count = Files.Count();
        for (int Index = 0; Index < Count; Index++)
        {
            if (guIsValidAudioFile(Files[Index]))
            {
                int TrackId = FindTrackFile(Files[Index], nullptr);
                if (TrackId)
                    Tracks.Add(TrackId);
            } else if (guIsValidImageFile(Files[Index].Lower()))
            {
                query = wxString::Format(
                    wxT("SELECT cover_id FROM covers WHERE cover_path = '%s'"), escape_query_str(Files[Index]).c_str());
                dbRes = ExecuteQuery(query);
                if (dbRes.NextRow())
                    Covers.Add(dbRes.GetInt(0));

                dbRes.Finalize();
            }
        }

        CleanItems(Tracks, Covers);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::CleanItems(const wxArrayInt &tracks, const wxArrayInt &covers)
    {
        wxString query;

        if (tracks.Count())
        {
            query = wxT("DELETE FROM songs WHERE ") + ArrayToFilter(tracks, wxT("song_id"));
            ExecuteUpdate(query);
        }

        if (covers.Count())
        {
            query = wxT("DELETE FROM covers WHERE ") + ArrayToFilter(covers, wxT("cover_id"));
            ExecuteUpdate(query);
        }

        // Delete all posible orphan entries
        //  query = wxT( "DELETE FROM genres WHERE genre_id NOT IN ( SELECT DISTINCT song_genreid FROM songs );" );
        //  ExecuteUpdate( query );
        //  query = wxT( "DELETE FROM artists WHERE artist_id NOT IN ( SELECT DISTINCT song_artistid FROM songs );" );
        //  ExecuteUpdate( query );
        //  query = wxT( "DELETE FROM albums WHERE album_id NOT IN ( SELECT DISTINCT song_albumid FROM songs );" );
        //  ExecuteUpdate( query );
        query = wxT("DELETE FROM covers WHERE cover_id NOT IN ( SELECT DISTINCT song_coverid FROM songs );");
        ExecuteUpdate(query);
        //  query = wxT( "DELETE FROM paths WHERE path_id NOT IN ( SELECT DISTINCT song_pathid FROM songs );" );
        //  ExecuteUpdate( query );
        query = wxT(
            "DELETE FROM plsets WHERE plset_type = 0 AND plset_songid NOT IN ( SELECT DISTINCT song_id FROM songs );");
        ExecuteUpdate(query);
        query = wxT("DELETE FROM settags WHERE settag_songid NOT IN ( SELECT DISTINCT song_id FROM songs );");
        ExecuteUpdate(query);

        //LoadCache();
    }

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::CheckDbVersion()
    {
        wxArrayString query;
        unsigned long dbVer;
        bool NeedVacuum = false;

        dbVer = GetDbVersion();
        guLogMessage(wxT("Library Db Version %lu"), dbVer);

        switch (dbVer)
        {
            case 4:
            {
                Close();
                guLogMessage(wxT("Update of database needed. Old database renamed to guayadeque.db.save"));
                wxRenameFile(m_DbName, m_DbName + wxT(".save"), true);
                m_NeedUpdate = true;
                Open(m_DbName);
                query.Add(wxT("CREATE TABLE IF NOT EXISTS Version( version INTEGER );"));
            }

            case 0:
            {
                // the database is going to be created... Reset the last update
                guConfig *Config = (guConfig *) guConfig::Get();
                if (Config)
                    Config->WriteStr(CONFIG_KEY_GENERAL_LAST_UPDATE, wxEmptyString, CONFIG_PATH_GENERAL);

                query.Add(wxT("CREATE TABLE IF NOT EXISTS songs( song_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                    "song_name VARCHAR COLLATE NOCASE, song_genreid INTEGER, song_genre VARCHAR COLLATE NOCASE, "
                    "song_artistid INTEGER, song_artist VARCHAR COLLATE NOCASE, song_albumartistid INTEGER, "
                    "song_albumartist VARCHAR COLLATE NOCASE, song_composerid INTEGER, "
                    "song_composer VARCHAR COLLATE NOCASE, song_albumid INTEGER, song_album VARCHAR COLLATE NOCASE, "
                    "song_pathid INTEGER, song_path VARCHAR, song_filename VARCHAR, "
                    "song_format VARCHAR(8) COLLATE NOCASE, song_disk VARCHAR COLLATE NOCASE, "
                    "song_number INTEGER(3), song_year INTEGER(4), song_comment VARCHAR COLLATE NOCASE, "
                    "song_coverid INTEGER, song_offset INTEGER, song_length INTEGER, song_bitrate INTEGER, "
                    "song_rating INTEGER DEFAULT -1, song_playcount INTEGER DEFAULT 0, song_addedtime INTEGER, "
                    "song_lastplay INTEGER, song_filesize INTEGER, song_albumsku VARCHAR, song_coverlink VARCHAR );"));

                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_name on songs( song_name ASC )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_albumid on songs( song_albumid,song_artist,song_year DESC, song_album, song_disk )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_composerid on songs( song_composerid, song_composer )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_artistid on songs( song_artistid, song_artist )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_albumartistid on songs( song_albumartistid, song_albumartist )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_genreid on songs( song_genreid, song_genre )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_pathid on songs( song_pathid ASC )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_length on songs( song_length ASC )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_bitrate on songs( song_bitrate ASC )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_rating on songs( song_rating ASC )"));
                //query.Add( wxT( "CREATE INDEX IF NOT EXISTS song_rating_desc ON songs( song_rating DESC )" ) );
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_playcount on songs( song_playcount ASC )"));
                //query.Add( wxT( "CREATE INDEX IF NOT EXISTS song_addedtime on songs( song_addedtime ASC )" ) );
                //query.Add( wxT( "CREATE INDEX IF NOT EXISTS song_addedtime_desc ON songs( song_addedtime DESC )" ) );
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_lastplay on songs( song_lastplay ASC )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_composerid on songs( song_composerid ASC )"));
                //query.Add( wxT( "CREATE INDEX IF NOT EXISTS song_composerid_desc ON songs( song_composerid DESC )" ) );
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_number ON songs( song_number )"));
                //query.Add( wxT( "CREATE INDEX IF NOT EXISTS song_path ON songs( song_path ASC )" ) );
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_path_filename ON songs( song_path, song_filename )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_format ON songs( song_format ASC )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_genre ON songs( song_genre,song_composer,song_artist,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_genre_desc ON songs( song_genre DESC,song_composer,song_artist,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_composer ON songs( song_composer,song_artist,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_composer_desc ON songs( song_composer DESC,song_artist,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_artist ON songs( song_artist,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_artist_desc ON songs( song_artist DESC,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_albumartist ON songs( song_albumartist,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_albumartist_desc ON songs( song_albumartist DESC,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_album ON songs( song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_album_desc ON songs( song_album DESC,song_disk,song_albumid,song_number )"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS song_disk ON songs( song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_disk_desc ON songs( song_disk DESC,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_year ON songs( song_year,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_year_desc ON songs( song_year DESC,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_addedtime ON songs( song_addedtime,song_album,song_disk,song_albumid,song_number )"));
                query.Add(wxT(
                    "CREATE INDEX IF NOT EXISTS song_addedtime_desc ON songs( song_addedtime DESC,song_album,song_disk,song_albumid,song_number )"));

                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS tags( tag_id INTEGER PRIMARY KEY AUTOINCREMENT, tag_name VARCHAR COLLATE NOCASE );"));
                //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'tag_id' on tags (tag_id ASC);" ) );
                query.Add(wxT("CREATE UNIQUE INDEX IF NOT EXISTS 'tag_name' on tags (tag_name ASC);"));

                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS settags( settag_tagid INTEGER, settag_artistid INTEGER, settag_albumid INTEGER, settag_songid INTEGER );"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS 'settag_tagid' on settags (settag_tagid ASC);"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS 'settag_artistid' on settags (settag_artistid ASC);"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS 'settag_albumid' on settags (settag_albumid ASC);"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS 'settag_songid' on settags (settag_songid ASC);"));

                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS playlists( playlist_id INTEGER PRIMARY KEY AUTOINCREMENT, playlist_name VARCHAR COLLATE NOCASE, "
                    "playlist_type INTEGER(2), playlist_limited BOOLEAN, playlist_limitvalue INTEGER, playlist_limittype INTEGER(2), "
                    "playlist_sorted BOOLEAN, playlist_sorttype INTEGER(2), playlist_sortdesc BOOLEAN, playlist_anyoption BOOLEAN, "
                    "playlist_path VARCHAR );"));
                //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'playlist_id' on playlists (playlist_id ASC);" ) );

                wxString querystr = wxT("INSERT INTO playlists(playlist_id, playlist_name, playlist_type, "
                    "playlist_limited, playlist_limitvalue, playlist_limittype, "
                    "playlist_sorted, playlist_sorttype, playlist_sortdesc, playlist_anyoption) "
                    "VALUES(NULL, '");
                querystr += _("Recently added tracks");
                querystr += wxT("', 1, 0, 0, 0, 1, 12, 1, 0 );");
                query.Add(querystr);
                querystr = wxT("INSERT INTO playlists(playlist_id, playlist_name, playlist_type, "
                    "playlist_limited, playlist_limitvalue, playlist_limittype, "
                    "playlist_sorted, playlist_sorttype, playlist_sortdesc, playlist_anyoption) "
                    "VALUES(NULL, '");

                querystr += _("Last played tracks");
                querystr += wxT("', 1, 0, 0, 0, 1, 11, 1, 0 );");
                query.Add(querystr);
                querystr = wxT("INSERT INTO playlists(playlist_id, playlist_name, playlist_type, "
                    "playlist_limited, playlist_limitvalue, playlist_limittype, "
                    "playlist_sorted, playlist_sorttype, playlist_sortdesc, playlist_anyoption) "
                    "VALUES(NULL, '");
                querystr += _("Best rated tracks");
                querystr += wxT("', 1, 0, 0, 0, 1, 8, 1, 0 );");
                query.Add(querystr);

                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS plsets(plset_id INTEGER PRIMARY KEY AUTOINCREMENT, plset_plid INTEGER, plset_songid INTEGER, "
                    "plset_type INTEGER(2), plset_option INTEGER(2), plset_text TEXT(255), plset_number INTEGER, plset_option2 INTEGER );"));
                //query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'plset_id' on plsets (plset_id ASC);" ) );
                query.Add(wxT("CREATE INDEX IF NOT EXISTS 'plset_plid' on plsets (plset_plid ASC);"));
                query.Add(wxT("INSERT INTO plsets(plset_id, plset_plid, plset_songid, "
                    "plset_type, plset_option, plset_text, plset_number, plset_option2 ) "
                    "VALUES(NULL, 1, 0, 14, 0, '', 1, 3 );"));
                query.Add(wxT("INSERT INTO plsets(plset_id, plset_plid, plset_songid, "
                    "plset_type, plset_option, plset_text, plset_number, plset_option2 ) "
                    "VALUES(NULL, 2, 0, 13, 0, '', 1, 2 );"));
                query.Add(wxT("INSERT INTO plsets(plset_id, plset_plid, plset_songid, "
                    "plset_type, plset_option, plset_text, plset_number, plset_option2 ) "
                    "VALUES(NULL, 3, 0, 10, 2, '', 4, 0 );"));

                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS covers( cover_id INTEGER PRIMARY KEY AUTOINCREMENT, cover_path VARCHAR, cover_thumb BLOB, cover_midsize BLOB, cover_hash VARCHAR );"));
                //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'cover_id' on covers (cover_id ASC);" ) );

                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS audioscs( audiosc_id INTEGER PRIMARY KEY AUTOINCREMENT, audiosc_artist VARCHAR(255), audiosc_album varchar(255), audiosc_track varchar(255), audiosc_playedtime INTEGER, audiosc_source char(1), audiosc_ratting char(1), audiosc_len INTEGER, audiosc_tracknum INTEGER, audiosc_mbtrackid INTEGER );"));
                //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'audiosc_id' on audioscs (audiosc_id ASC);" ) );
                query.Add(wxT("CREATE INDEX IF NOT EXISTS 'audiosc_playedtime' on audioscs (audiosc_playedtime ASC);"));

                query.Add(wxT("CREATE TABLE IF NOT EXISTS deleted( delete_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                    "delete_path VARCHAR, delete_date INTEGER );"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS delete_path on deleted(delete_path ASC);"));
                query.Add(wxT("CREATE INDEX IF NOT EXISTS delete_date on deleted(delete_date ASC);"));
            }

            case 5:
            {
                //      query.Add( wxT( "CREATE TABLE IF NOT EXISTS podcastchs( podcastch_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                //                      "podcastch_url VARCHAR, podcastch_title VARCHAR COLLATE NOCASE, podcastch_description VARCHAR, "
                //                      "podcastch_language VARCHAR, podcastch_time INTEGER, podcastch_sumary VARCHAR, "
                //                      "podcastch_author VARCHAR, podcastch_ownername VARCHAR, podcastch_owneremail VARCHAR, "
                //                      "podcastch_category VARCHAR, podcastch_image VARCHAR, podcastch_downtype INTEGER, "
                //                      "podcastch_downtext VARCHAR, podcastch_allowdel BOOLEAN );" ) );
                //      //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'podcastch_id' on podcastchs(podcastch_id ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastch_title' on podcastchs(podcastch_title ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastch_url' on podcastchs(podcastch_url ASC);" ) );
                //
                //      query.Add( wxT( "CREATE TABLE IF NOT EXISTS podcastitems( podcastitem_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                //                      "podcastitem_chid INTEGER, podcastitem_title VARCHAR COLLATE NOCASE, podcastitem_summary VARCHAR, "
                //                      "podcastitem_author VARCHAR COLLATE NOCASE, podcastitem_enclosure VARCHAR, podcastitem_time INTEGER, "
                //                      "podcastitem_file VARCHAR, podcastitem_filesize INTEGER, podcastitem_length INTEGER, "
                //                      "podcastitem_addeddate INTEGER, podcastitem_playcount INTEGER, "
                //                      "podcastitem_lastplay INTEGER, podcastitem_status INTEGER );" ) );
                //      //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'podcastitem_id' on podcastitems(podcastitem_id ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_title' on podcastitems(podcastitem_title ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_file' on podcastitems(podcastitem_file ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_chid' on podcastitems(podcastitem_chid ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_time' on podcastitems(podcastitem_time ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_enclosure' on podcastitems(podcastitem_enclosure ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_author' on podcastitems(podcastitem_author ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'podcastitem_length' on podcastitems(podcastitem_length ASC);" ) );
            }

            case 6:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("DROP INDEX 'radiostation_id';"));
                    query.Add(wxT("DROP INDEX 'radiostation_genreid';"));
                    query.Add(wxT("DROP TABLE 'radiostations';"));
                    query.Add(wxT(
                        "CREATE TABLE IF NOT EXISTS radiostations( radiostation_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                        "radiostation_scid INTEGER, radiostation_source INTEGER, radiostation_genreid INTEGER, "
                        "radiostation_name VARCHAR, radiostation_link VARCHAR, radiostation_type VARCHAR, "
                        "radiostation_br INTEGER, radiostation_lc INTEGER, radiostation_ct VARCHAR );"));
                    //query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'radiostation_id' on radiostations (radiostation_id ASC);" ) );
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_genreid' on radiostations (radiostation_source,radiostation_genreid ASC);"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_lc' on radiostations (radiostation_lc ASC);"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_type' on radiostations (radiostation_type ASC);"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_ct' on radiostations (radiostation_ct ASC);"));
                }
            }

            case 7:
            {
                query.Add(wxT("DROP TABLE 'covers';"));
                query.Add(wxT(
                    "CREATE TABLE IF NOT EXISTS covers(cover_id INTEGER PRIMARY KEY AUTOINCREMENT, cover_path VARCHAR(1024), cover_thumb BLOB, cover_midsize BLOB, cover_hash VARCHAR(32));"));
                //query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'cover_id' on covers (cover_id ASC);" ) );
            }

            case 8:
            {
                query.Add(wxT("DELETE FROM covers;"));
            }

            case 9:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("ALTER TABLE songs ADD COLUMN song_disk VARCHAR"));
                    query.Add(wxT("ALTER TABLE songs ADD COLUMN song_comment VARCHAR"));
                    //query.Add( wxT( "ALTER TABLE songs ADD COLUMN song_composer VARCHAR(512)" ) );
                }
            }

            case 10:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT(
                        "UPDATE playlists SET playlist_sorttype = playlist_sorttype + 1 WHERE playlist_sorttype > 4;"));
                    query.Add(wxT("UPDATE plsets SET plset_type = plset_type + 2 WHERE plset_type > 4;"));
                }
            }

            case 11:
            {
                //      query.Add( wxT( "CREATE TABLE IF NOT EXISTS composers( composer_id INTEGER  PRIMARY KEY AUTOINCREMENT, composer_name varchar(512) );" ) );
                //      query.Add( wxT( "CREATE UNIQUE INDEX IF NOT EXISTS 'composer_id' on composers (composer_id ASC);" ) );
                //      query.Add( wxT( "CREATE INDEX IF NOT EXISTS 'composer_name' on composers (composer_name ASC);" ) );
                if (dbVer > 4)
                {
                    //query.Add( wxT( "ALTER TABLE songs DELETE COLUMN song_composer" ) );
                    query.Add(wxT("ALTER TABLE songs ADD COLUMN song_composerid INTEGER"));
                }
                m_NeedUpdate = true;
            }

            case 12:
            {
                if (dbVer > 4)
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS 'song_composerid' on songs( song_composerid ASC );"));
            }

            case 13:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("ALTER TABLE radiogenres ADD COLUMN radiogenre_source INTEGER"));
                    query.Add(wxT("ALTER TABLE radiogenres ADD COLUMN radiogenre_flags INTEGER"));
                    query.Add(wxT("UPDATE radiogenres SET radiogenre_source = 0;"));
                    query.Add(wxT("UPDATE radiogenres SET radiogenre_flags = 0;"));

                    query.Add(wxT("ALTER TABLE radiostations ADD COLUMN radiostation_source INTEGER"));
                    query.Add(wxT("UPDATE radiostations SET radiostation_source = radiostation_isuser;"));
                }
            }

            case 14:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("ALTER TABLE radiostations ADD COLUMN radiostation_ct VARCHAR"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_lc' on radiostations (radiostation_lc ASC);"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_type' on radiostations (radiostation_type ASC);"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS 'radiostation_ct' on radiostations (radiostation_ct ASC);"));
                }
            }

            case 15:
            {
                if (dbVer > 4)
                {
                    //query.Add( wxT( "VACUUM;" ) );
                    query.Add(wxT("CREATE TABLE IF NOT EXISTS songs1( song_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                        "song_name VARCHAR COLLATE NOCASE, song_genreid INTEGER, song_genre VARCHAR COLLATE NOCASE, "
                        "song_artistid INTEGER, song_artist VARCHAR COLLATE NOCASE, song_albumartistid INTEGER, "
                        "song_albumartist VARCHAR COLLATE NOCASE, song_composerid INTEGER, song_composer VARCHAR COLLATE NOCASE, "
                        "song_albumid INTEGER, song_album VARCHAR COLLATE NOCASE, song_pathid INTEGER, song_path VARCHAR, "
                        "song_filename VARCHAR, song_format VARCHAR(8) COLLATE NOCASE, song_disk VARCHAR COLLATE NOCASE, "
                        "song_number INTEGER(3), song_year INTEGER(4), song_comment VARCHAR COLLATE NOCASE, "
                        "song_coverid INTEGER, song_offset INTEGER, song_length INTEGER, song_bitrate INTEGER, "
                        "song_rating INTEGER DEFAULT -1, song_playcount INTEGER DEFAULT 0, song_addedtime INTEGER, "
                        "song_lastplay INTEGER, song_filesize INTEGER );"));
                    query.Add(wxT(
                        "INSERT INTO songs1( song_id, song_name, song_albumid, song_artistid, song_genreid, song_filename, "
                        "song_pathid, song_number, song_disk, song_year, song_comment, song_composerid, song_length, "
                        "song_bitrate, song_rating, song_playcount, song_addedtime, song_lastplay, song_filesize ) "
                        "SELECT song_id, song_name, song_albumid, song_artistid, song_genreid, song_filename, song_pathid, "
                        "song_number, song_disk, song_year, song_comment, song_composerid, song_length, song_bitrate, "
                        "song_rating, song_playcount, song_addedtime, song_lastplay, song_filesize FROM songs"));
                    query.Add(wxT(
                        "UPDATE songs1 SET song_genre = ( SELECT genre_name FROM genres WHERE song_genreid = genre_id )"));
                    query.Add(wxT(
                        "UPDATE songs1 SET song_composer = ( SELECT composer_name FROM composers WHERE song_composerid = composer_id )"));
                    query.Add(wxT(
                        "UPDATE songs1 SET song_artist = ( SELECT artist_name FROM artists WHERE song_artistid = artist_id )"));
                    query.Add(wxT(
                        "UPDATE songs1 SET song_album = ( SELECT album_name FROM albums WHERE song_albumid = album_id )"));
                    query.Add(wxT(
                        "UPDATE songs1 SET song_path = ( SELECT path_value FROM paths WHERE song_pathid = path_id )"));
                    query.Add(wxT(
                        "UPDATE songs1 SET song_coverid = ( SELECT album_coverid FROM albums WHERE song_albumid = album_id )"));
                    query.Add(wxT("UPDATE songs1 SET song_offset = 0"));
                    query.Add(wxT("DROP TABLE songs"));
                    query.Add(wxT("ALTER TABLE songs1 RENAME TO songs"));

                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_name on songs( song_name ASC )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_albumid on songs( song_albumid,song_artist,song_year DESC, song_album, song_disk )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_composerid on songs( song_composerid, song_composer )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_artistid on songs( song_artistid, song_artist )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_albumartistid on songs( song_albumartistid, song_albumartist )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_genreid on songs( song_genreid, song_genre )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_pathid on songs( song_pathid ASC )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_length on songs( song_length ASC )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_bitrate on songs( song_bitrate ASC )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_rating on songs( song_rating ASC )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_playcount on songs( song_playcount ASC )"));
                    //query.Add( wxT( "CREATE INDEX IF NOT EXISTS song_addedtime on songs( song_addedtime ASC )" ) );
                    //query.Add( wxT( "CREATE INDEX song_addedtime_desc ON songs( song_addedtime DESC )" ) );
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_lastplay on songs( song_lastplay ASC )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_composerid on songs( song_composerid ASC )"));
                    //query.Add( wxT( "CREATE INDEX song_composerid_desc ON songs( song_composerid DESC )" ) );
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_number ON songs( song_number )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_path ON songs( song_path ASC )"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS song_format ON songs( song_format ASC )"));
                    //query.Add( wxT( "CREATE INDEX song_rating_desc ON songs( song_rating DESC )" ) );
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_genre ON songs( song_genre,song_composer,song_artist,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_genre_desc ON songs( song_genre DESC,song_composer,song_artist,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_composer ON songs( song_composer,song_artist,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_composer_desc ON songs( song_composer DESC,song_artist,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_artist ON songs( song_artist,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_artist_desc ON songs( song_artist DESC,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_albumartist ON songs( song_albumartist,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_albumartist_desc ON songs( song_albumartist DESC,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_album ON songs( song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_album_desc ON songs( song_album DESC,song_disk,song_albumid,song_number )"));
                    query.Add(
                        wxT("CREATE INDEX IF NOT EXISTS song_disk ON songs( song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_disk_desc ON songs( song_disk DESC,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_year ON songs( song_year,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_year_desc ON songs( song_year DESC,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_addedtime ON songs( song_addedtime,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_addedtime_desc ON songs( song_addedtime DESC,song_album,song_disk,song_albumid,song_number )"));
                    NeedVacuum = (dbVer > 4);
                }
            }

            case 16: // Added Album Artist to dynamic playlist
            {
                if (dbVer > 4)
                {
                    query.Add(wxT(
                        "UPDATE playlists SET playlist_sorttype = playlist_sorttype + 1 WHERE playlist_sorttype > 1;"));
                    query.Add(wxT("UPDATE plsets SET plset_type = plset_type + 1 WHERE plset_type > 1;"));
                    query.Add(wxT("UPDATE plsets SET plset_option = plset_option + 1 WHERE "
                        "( plset_option > 2 AND plset_type IN ( 9 ) ) OR"
                        "( plset_option > 0 AND plset_type IN ( 9, 10, 11 ) );"));
                }
            }

            case 17: // Changed addedtime index to sort by album, disk, track also
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("DROP INDEX song_addedtime;"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_addedtime ON songs( song_addedtime,song_album,song_disk,song_albumid,song_number )"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_addedtime_desc ON songs( song_addedtime DESC,song_album,song_disk,song_albumid,song_number )"));

                    query.Add(wxT("CREATE TABLE IF NOT EXISTS deleted( delete_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                        "delete_path VARCHAR, delete_date INTEGER );"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS delete_path on deleted(delete_path ASC);"));
                    query.Add(wxT("CREATE INDEX IF NOT EXISTS delete_date on deleted(delete_date ASC);"));
                }
            }

            case 18:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("DROP INDEX song_path;"));
                    query.Add(wxT(
                        "CREATE INDEX IF NOT EXISTS song_path_filename ON songs( song_path, song_filename )"));
                    query.Add(wxT("CREATE UNIQUE INDEX IF NOT EXISTS 'tag_name' on tags (tag_name ASC);"));
                }
            }

            case 19:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("ALTER TABLE songs ADD COLUMN song_albumsku VARCHAR"));
                    query.Add(wxT("ALTER TABLE songs ADD COLUMN song_coverlink VARCHAR"));
                }
            }

            case 20:
            {
                if (dbVer > 4)
                {
                    query.Add(wxT("ALTER TABLE playlists ADD COLUMN playlist_path VARCHAR"));

                    query.Add(wxT("DROP INDEX IF EXISTS 'tag_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'playlist_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'plset_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'cover_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'audiosc_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'radiogenre_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'radiostation_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'radiolabel_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'radiosetlabel_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'podcastch_id'"));
                    query.Add(wxT("DROP INDEX IF EXISTS 'podcastitem_id'"));
                }

                guLogMessage(wxT("Updating database version to " GU_CURRENT_DBVERSION));
                query.Add(wxT("DELETE FROM Version;"));
                query.Add(wxT("INSERT INTO Version( version ) VALUES( " GU_CURRENT_DBVERSION " );"));
            }

            default:
                break;
        }

        if (NeedVacuum)
            query.Add(wxT("VACUUM;"));

        int Count = query.Count();
        for (int Index = 0; Index < Count; Index++)
        {
            try
            {
                //guLogMessage( wxT( "%s" ), query[ Index ].c_str() );
                ExecuteUpdate(query[Index]);
            } catch (wxSQLite3Exception &e)
            {
                guLogError(wxT("%u: %s"), e.GetErrorCode(), e.GetMessage().c_str());
            }
            catch (...)
            {
                guLogError(wxT("Error executing query %s"), query[Index].c_str());
            }
        }

        return true;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetGenreId(wxString &genrename)
    {
        if (m_LastGenre == genrename)
            return m_LastGenreId;

        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;

        m_LastGenre = genrename;

        query = wxString::Format(wxT("SELECT song_genreid FROM songs WHERE song_genre = '%s' LIMIT 1;"),
                                 escape_query_str(genrename).c_str());

        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
            RetVal = m_LastGenreId = dbRes.GetInt(0);
        else
        {
            query = wxT("SELECT MAX(song_genreid) FROM songs;");

            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                RetVal = m_LastGenreId = dbRes.GetInt(0) + 1;
            else
                RetVal = m_LastGenreId = 1;
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetComposerId(wxString &composername, bool create)
    {
        if (m_LastComposer == composername)
            return m_LastComposerId;

        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;

        m_LastComposer = composername;

        query = wxString::Format(wxT("SELECT song_composerid FROM songs WHERE song_composer = '%s' LIMIT 1;"),
                                 escape_query_str(composername).c_str());

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = m_LastComposerId = dbRes.GetInt(0);
        else if (create)
        {
            query = wxT("SELECT MAX(song_composerid) FROM songs;");

            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                RetVal = m_LastComposerId = dbRes.GetInt(0) + 1;
            else
                RetVal = m_LastComposerId = 1;
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    wxBitmap *guDbLibrary::GetCoverBitmap(const int coverid, const bool thumb)
    {
        if (coverid <= 0)
            return nullptr;

        wxImage *Img = nullptr;
        wxBitmap *RetVal = nullptr;
        wxString query;
        wxSQLite3ResultSet dbRes;
        const unsigned char *Data;
        int DataLen = 0;

        query = wxT("SELECT ");
        query += thumb ? wxT("cover_thumb") : wxT("cover_midsize");
        query += wxString::Format(wxT(" FROM covers WHERE cover_id = %u LIMIT 1;"), coverid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            Data = dbRes.GetBlob(0, DataLen);

            if (DataLen)
            {
                //guLogMessage( wxT( "Read %i bytes for image %i" ), len, CoverId );
                unsigned char *ImgData = (unsigned char *) malloc(DataLen);
                memcpy(ImgData, Data, DataLen);

                int ImgSize = thumb ? GUCOVER_THUMB_SIZE : GUCOVER_IMAGE_SIZE;
                Img = new wxImage(ImgSize, ImgSize, ImgData);
                //TmpImg->SaveFile( wxString::Format( wxT( "/home/jrios/%u.jpg" ), CoverId ), wxBITMAP_TYPE_JPEG );
                RetVal = new wxBitmap(*Img);
                delete Img;
            }
        }

        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::AddCoverFile(const wxString &coverfile, const wxString &coverhash)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int CoverId = 0;
        wxString CoverFile;

        //guLogMessage( wxT( "AddCoverFile( '%s', '%s' )" ), coverfile.c_str(), coverhash.c_str() );

        // Create the Thumb image
        wxImage TmpImg;

        TmpImg.LoadFile(coverfile);
        if (TmpImg.IsOk())
        {
            wxString CoverHash;
            if (coverhash.IsEmpty())
            {
                guMD5 md5;
                CoverHash = md5.MD5File(coverfile);
            } else
                CoverHash = coverhash;

            //guLogWarning( _T( "Scaling image %i" ), n );
            TmpImg.Rescale(GUCOVER_THUMB_SIZE, GUCOVER_THUMB_SIZE, wxIMAGE_QUALITY_HIGH);
            if (TmpImg.IsOk())
            {
                //      wxFileOutputStream FOut( wxString::Format( wxT( "/home/jrios/%s.jpg" ), coverhash.c_str() ) );
                //      TmpImg.SaveFile( FOut, wxBITMAP_TYPE_JPEG );
                //      FOut.Close();
                wxImage MidImg;
                MidImg.LoadFile(coverfile);
                if (MidImg.IsOk())
                {
                    MidImg.Rescale(GUCOVER_IMAGE_SIZE, GUCOVER_IMAGE_SIZE, wxIMAGE_QUALITY_HIGH);

                    wxSQLite3Statement stmt = m_Db->PrepareStatement(wxString::Format(wxT(
                        "INSERT INTO covers( cover_id, cover_path, cover_thumb, cover_midsize, cover_hash ) "
                        "VALUES(NULL, '%s', ?, ?, '%s' );"), escape_query_str(coverfile).c_str(), CoverHash.c_str()));
                    try
                    {
                        stmt.Bind(1, TmpImg.GetData(), TmpImg.GetWidth() * TmpImg.GetHeight() * 3);
                        stmt.Bind(2, MidImg.GetData(), MidImg.GetWidth() * MidImg.GetHeight() * 3);

                        //guLogMessage( wxT( "AddCoverFile: %s" ), stmt.GetSQL().c_str() );
                        stmt.ExecuteQuery();
                    } catch (wxSQLite3Exception &e)
                    {
                        guLogError(wxT("%u: %s"), e.GetErrorCode(), e.GetMessage().c_str());
                    }
                    catch (...)
                    {
                        guLogError(wxT("Error inserting the image %s"), coverfile.c_str());
                    }

                    CoverId = GetLastRowId();
                } else
                    guLogError(wxT("Error resizing to %i"), GUCOVER_IMAGE_SIZE);
            } else
                guLogError(wxT("Error resizing to %i"), GUCOVER_THUMB_SIZE);
        } else
            guLogError(wxT("Invalid cover file '%s'"), coverfile.c_str());

        return CoverId;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::AddCoverImage(const wxImage &image)
    {
        //guLogMessage( wxT( "guDbLibrary::AddCoverImage" ) );
        wxString query;
        wxSQLite3ResultSet dbRes;
        int CoverId = 0;
        wxString CoverFile;

        wxImage MidImg = image;
        MidImg.Rescale(GUCOVER_IMAGE_SIZE, GUCOVER_IMAGE_SIZE, wxIMAGE_QUALITY_HIGH);
        if (MidImg.IsOk())
        {
            wxImage TinyImg = image;
            TinyImg.Rescale(GUCOVER_THUMB_SIZE, GUCOVER_THUMB_SIZE, wxIMAGE_QUALITY_HIGH);
            if (TinyImg.IsOk())
            {
                wxSQLite3Statement stmt = m_Db->PrepareStatement(wxT(
                    "INSERT INTO covers( cover_id, cover_path, cover_thumb, cover_midsize, cover_hash ) "
                    "VALUES(NULL, '', ?, ?, '' );"));
                try
                {
                    stmt.Bind(1, TinyImg.GetData(), TinyImg.GetWidth() * TinyImg.GetHeight() * 3);
                    stmt.Bind(2, MidImg.GetData(), MidImg.GetWidth() * MidImg.GetHeight() * 3);

                    //guLogMessage( wxT( "AddCoverFile: %s" ), stmt.GetSQL().c_str() );
                    stmt.ExecuteQuery();
                } catch (wxSQLite3Exception &e)
                {
                    guLogError(wxT("%u: %s"), e.GetErrorCode(), e.GetMessage().c_str());
                }
                catch (...)
                {
                    guLogError(wxT("Error inserting the image"));
                }

                CoverId = GetLastRowId();
            } else
                guLogError(wxT("Error resizing to %i"), GUCOVER_THUMB_SIZE);
        } else
            guLogError(wxT("Error resizing to %i"), GUCOVER_IMAGE_SIZE);

        return CoverId;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateCoverFile(int coverid, const wxString &coverfile, const wxString &coverhash)
    {
        wxString query;
        //int CoverId = 0;
        wxString CoverFile;

        // Create the Thumb image
        wxImage TmpImg;

        TmpImg.LoadFile(coverfile);
        if (TmpImg.IsOk())
        {
            //guLogWarning( _T( "Scaling image %i" ), n );
            TmpImg.Rescale(GUCOVER_THUMB_SIZE, GUCOVER_THUMB_SIZE, wxIMAGE_QUALITY_HIGH);

            if (TmpImg.IsOk())
            {
                wxImage MidImg;
                MidImg.LoadFile(coverfile);
                MidImg.Rescale(GUCOVER_IMAGE_SIZE, GUCOVER_IMAGE_SIZE, wxIMAGE_QUALITY_HIGH);
                if (MidImg.IsOk())
                {
                    //guLogWarning( wxT( "Cover Image w:%u h:%u " ), TmpImg.GetWidth(), TmpImg.GetHeight() );
                    wxSQLite3Statement stmt = m_Db->PrepareStatement(wxString::Format(
                        wxT(
                            "UPDATE covers SET cover_thumb = ?, cover_midsize = ?, cover_hash = '%s' WHERE cover_id = %u;"),
                        coverhash.c_str(), coverid));

                    try
                    {
                        stmt.Bind(1, TmpImg.GetData(), TmpImg.GetWidth() * TmpImg.GetHeight() * 3);
                        stmt.Bind(2, MidImg.GetData(), MidImg.GetWidth() * MidImg.GetHeight() * 3);
                        stmt.ExecuteQuery();
                    } catch (wxSQLite3Exception &e)
                    {
                        guLogMessage(wxT("%u: %s"), e.GetErrorCode(), e.GetMessage().c_str());
                    }
                }
            }
        } else
            guLogError(wxT("Invalid cover file '%s'"), coverfile.c_str());
    }

    //// -------------------------------------------------------------------------------- //
    //int guDbLibrary::FindCoverFile( const wxString &dirname )
    //{
    //    wxString query;
    //    wxSQLite3ResultSet dbRes;
    //    wxDir Dir;
    //    wxString FileName;
    //    wxString CurFile;
    //    wxString DirName = dirname;
    //    int CoverId = 0;
    //
    //    if( !DirName.EndsWith( wxT( "/" ) ) )
    //        DirName += wxT( "/" );
    //
    //    Dir.Open( DirName );
    //    //wxSetWorkingDirectory( DirName );
    //
    //    if( Dir.IsOpened() )
    //    {
    //        if( Dir.GetFirst( &FileName, wxEmptyString, wxDIR_FILES ) )
    //        {
    //            do {
    //                CurFile = FileName.Lower();
    //                //guLogMessage( wxT( "FindCoverFile: Found file '%s'" ), FileName.c_str() );
    //                if( SearchCoverWords( CurFile, m_CoverSearchWords ) )
    //                {
    //                    //guLogMessage( wxT( "FindCoverFile: This file have been detected as a Cover" ) );
    //                    if( CurFile.EndsWith( wxT( ".jpg" ) ) ||
    //                        CurFile.EndsWith( wxT( ".jpeg" ) ) ||
    //                        CurFile.EndsWith( wxT( ".png" ) ) ||
    //                        CurFile.EndsWith( wxT( ".bmp" ) ) ||
    //                        CurFile.EndsWith( wxT( ".gif" ) ) )
    //                    {
    //                        //guLogMessage( wxT( "FindCoverFile: This file looks like an image file" ) );
    //                        CurFile = DirName + FileName;
    //                        //guLogMessage( wxT( "Found Cover: %s" ), CurFile.c_str() );
    //                        guMD5 md5;
    //                        wxString CoverHash = md5.MD5File( CurFile );
    //
    //                        escape_query_str( &CurFile );
    //
    //                        query = wxString::Format( wxT( "SELECT cover_id, cover_path, cover_hash FROM covers ""WHERE cover_path = '%s' LIMIT 1;" ), CurFile.c_str() );
    //
    //                        dbRes = ExecuteQuery( query );
    //
    //                        if( dbRes.NextRow() ) // The cover is found in the database
    //                        {
    //                            CoverId = dbRes.GetInt( 0 );
    //                            // Check if the file have been changed
    //                            if( dbRes.GetString( 2 ) != CoverHash )
    //                            {
    //                                // The cover is different. Update the thumb is needed
    //                                UpdateCoverFile( CoverId, DirName + FileName, CoverHash );
    //                            }
    //                        }
    //                        else
    //                        {
    //                            CoverId = AddCoverFile( DirName + FileName, CoverHash );
    //                        }
    //                        break;
    //                    }
    //                }
    //            } while( Dir.GetNext( &FileName ) );
    //        }
    //    }
    //    //wxSetWorkingDirectory( SavedDir );
    //    return CoverId;
    //}

    // -------------------------------------------------------------------------------- //
    wxString guDbLibrary::GetCoverPath(const int CoverId)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxString RetVal = wxEmptyString;
        if (m_LastCoverId != CoverId)
        {
            int count = m_LastItems.Count();
            if (count)
            {
                for (int index = 0; index < count; index++)
                {
                    if (m_LastItems[index].m_Id == CoverId)
                    {
                        m_LastCoverId = CoverId;
                        m_LastCoverPath = m_LastItems[index].m_Name;
                        return m_LastCoverPath;
                    }
                }
            }

            if (count > 25) // MAX_CACHE_ITEMS
                m_LastItems.RemoveAt(0);

            query = wxString::Format(wxT("SELECT cover_path, cover_hash FROM covers "
                "WHERE cover_id = %u "
                "LIMIT 1;"), CoverId);
            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
            {
                RetVal = dbRes.GetString(0);
                if (!RetVal.IsEmpty())
                {
                    // Check if the cover have been updated
                    guMD5 md5;
                    wxString CoverHash = md5.MD5File(RetVal);
                    if (CoverHash != dbRes.GetString(1))
                        UpdateCoverFile(CoverId, RetVal, CoverHash);
                }
            }
            m_LastCoverId = CoverId;
            m_LastCoverPath = RetVal;
            m_LastItems.Add(new guListItem(CoverId, m_LastCoverPath));
            dbRes.Finalize();
        } else
            RetVal = m_LastCoverPath;
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::SetAlbumCover(const int AlbumId, const wxImage &image)
    {
        int CoverId = 0;
        wxSQLite3ResultSet dbRes;
        wxString query;
        wxString FileName;
        int RetVal = 0;

        CoverId = GetAlbumCoverId(AlbumId);

        if (CoverId > 0)
        {
            query = wxString::Format(wxT("DELETE FROM covers WHERE cover_id = %i;"), CoverId);
            ExecuteUpdate(query);
        }

        if (image.IsOk())
        {
            CoverId = AddCoverImage(image);
            query = wxString::Format(
                wxT("UPDATE songs SET song_coverid = %i WHERE song_albumid = %i;"), CoverId, AlbumId);
            RetVal = ExecuteUpdate(query);
        } else
        {
            query = wxString::Format(wxT("UPDATE songs SET song_coverid = 0 WHERE song_albumid = %i;"), AlbumId);
            //guLogMessage( query );
            CoverId = 0;
        }

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::SetAlbumCover(const int AlbumId, const wxString &CoverPath, const wxString &coverhash)
    {
        int CoverId = 0;
        wxSQLite3ResultSet dbRes;
        wxString query;
        wxString FileName;
        int RetVal = 0;

        //guLogMessage( wxT( "Updating album: %i path: '%s'" ), AlbumId, CoverPath.c_str() );
        // Delete the actual assigned Cover
        // Find the Cover assigned to the album
        CoverId = GetAlbumCoverId(AlbumId);

        if (CoverId > 0)
        {
            query = wxString::Format(wxT("DELETE FROM covers WHERE cover_id = %i;"), CoverId);
            ExecuteUpdate(query);
        }

        if (!CoverPath.IsEmpty())
        {
            //guLogMessage( wxT( "Setting new cover '%s'" ), CoverPath.c_str() );
            CoverId = AddCoverFile(CoverPath, coverhash);

            query = wxString::Format(
                wxT("UPDATE songs SET song_coverid = %i WHERE song_albumid = %i;"), CoverId, AlbumId);
            RetVal = ExecuteUpdate(query);
        } else
        {
            query = wxString::Format(wxT("UPDATE songs SET song_coverid = 0 WHERE song_albumid = %i;"), AlbumId);
            ExecuteUpdate(query);
            CoverId = 0;
        }

        //  // Update the AlbumsCache
        //  guAlbumItem * AlbumItem = guAlbumItemGetItem( m_AlbumsCache, AlbumId );
        //  if( AlbumItem )
        //  {
        //      AlbumItem->m_CoverId = CoverId;
        //      AlbumItem->m_CoverPath = CoverPath;
        //      //guLogMessage( wxT( "Updated Album with Cover %i '%s'" ), CoverId, CoverPath.c_str() );
        //  }
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumId(wxString &albumname, const int artistid, const int pathid,
                                const wxString &pathname, const int coverid)
    {
        //guLogMessage( wxT( "GetAlbumId : %s" ), m_LastAlbum.c_str() );
        if (m_LastAlbum == albumname)
        {
            //guLogMessage( wxT( "Album was found in cache" ) );
            return m_LastAlbumId;
        }

        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;

        query = wxString::Format(wxT("SELECT song_albumid, song_artistid "
            "FROM songs "
            "WHERE song_album = '%s' "
            "AND song_pathid = %u LIMIT 1;"),
                                 escape_query_str(albumname).c_str(), pathid);

        //guLogMessage( wxT( "%s" ), query.c_str() );

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            RetVal = m_LastAlbumId = dbRes.GetInt(0);

            //    // Now check if the artist id changed and if so update it
            //    if( dbRes.GetInt( 1 ) != artistid )
            //    {
            //        query = wxString::Format( wxT( "UPDATE songs SET song_artistid = %u "
            //                                       "WHERE album_id = %i;" ), artistid, RetVal );
            //        ExecuteUpdate( query );
            //    }
        } else
        {
            query = wxT("SELECT MAX(song_albumid) FROM songs;");

            dbRes = ExecuteQuery(query);

            if (dbRes.NextRow())
                RetVal = m_LastAlbumId = dbRes.GetInt(0) + 1;
            else
                RetVal = m_LastAlbumId = 1;
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    wxArrayInt guDbLibrary::GetLabelIds(const wxArrayString &Labels)
    {
        wxArrayInt RetVal;
        wxString LabelName;
        int LabelId;
        int count = Labels.Count();
        for (int index = 0; index < count; index++)
        {
            LabelName = Labels[index];
            if (GetLabelId(&LabelId, LabelName))
                RetVal.Add(LabelId);
        }
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetLabelId(int *LabelId, wxString &LabelName)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;
        //  printf( "GetLabelId\n" );

        escape_query_str(&LabelName);
        query = wxString::Format(wxT("SELECT tag_id FROM tags "
            "WHERE tag_name = '%s' LIMIT 1;"), LabelName.c_str());

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            *LabelId = dbRes.GetInt(0);
            RetVal = 1;
        } else
        {
            query = wxT("INSERT INTO tags( tag_id, tag_name ) VALUES(NULL, '") +
                    LabelName + wxT("');");

            if (ExecuteUpdate(query) == 1)
            {
                *LabelId = GetLastRowId();
                RetVal = 1;
            }
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::PathExists(const wxString &path)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;
        wxString PathValue = GetPathAddTrailSep(path);

        escape_query_str(&PathValue);

        query = wxString::Format(wxT("SELECT song_pathid FROM songs WHERE song_path = '%s' LIMIT 1;"),
                                 PathValue.c_str());
        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetInt(0);

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPathId(wxString &PathValue)
    {
        if (PathValue == m_LastPath)
            return m_LastPathId;

        int RetVal = 0;

        // TODO Add a Lock
        wxString query;
        wxSQLite3ResultSet dbRes;

        m_LastPath = PathValue;

        AddPathTrailSep(PathValue);

        query = wxString::Format(wxT("SELECT song_pathid FROM songs "
            "WHERE song_path = '%s' LIMIT 1"),
                                 escape_query_str(PathValue).c_str());
        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = m_LastPathId = dbRes.GetInt(0);
        else
        {
            dbRes.Finalize();
            query = wxT("SELECT MAX(song_pathid) FROM songs;");
            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                RetVal = m_LastPathId = dbRes.GetInt(0) + 1;
            else
                RetVal = m_LastPathId = 1;
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongId(wxString &filename, const int pathid, const time_t filedate, const int start,
                               bool *created)
    {
        //wxSQLite3StatementBuffer query;
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;

        //  printf( "GetSongId0\n" );
        //  printf( "%u : %s\n", FileName.Length(), TextBuf );

        query = query.Format(wxT("SELECT song_id FROM songs WHERE song_pathid = %u "
            "AND song_filename = '%s' AND song_offset = %i LIMIT 1;"),
                             pathid, escape_query_str(filename).c_str(), start);
        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetInt(0);
        else
        {
            query = query.Format(wxT(
                "INSERT INTO songs( song_id, song_pathid, song_rating, song_playcount, song_addedtime ) "
                "VALUES(NULL, %u, -1, 0, %lu )"), pathid, filedate);
            //guLogMessage( wxT( "Query: '%s'" ), query.c_str() );
            if (ExecuteUpdate(query))
                RetVal = GetLastRowId();
            if (created)
                *created = true;
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongId(wxString &FileName, wxString &FilePath, const time_t filedate, const int start,
                               bool *created)
    {
        return GetSongId(FileName, GetPathId(FilePath), filedate, start, created);
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::ReadFileTags(const wxString &filename, const bool allowrating)
    {
        if (guCuePlaylistFile::IsValidFile(filename)) // If its a cue playlist
        {
            //guLogMessage( wxT( "***** CUE FILE ***** '%s'" ), filename.c_str() );

            guCuePlaylistFile CuePlaylistFile(filename);
            int Count = CuePlaylistFile.Count();
            if (!Count)
                return 0;

            //guLogMessage( wxT( "With %i tracks" ), Count );
            for (int Index = 0; Index < Count; Index++)
            {
                guCuePlaylistItem &CueItem = CuePlaylistFile.GetItem(Index);
                //guLogMessage(wxT("Loading track %i '%s' - %i"), Index, CueItem.m_TrackPath.c_str(), CueItem.m_Length);

                if (wxFileExists(CueItem.m_TrackPath))
                {
                    wxString query = wxT("DELETE FROM songs WHERE song_filename = '");
                    query += escape_query_str(CueItem.m_TrackPath.AfterLast(wxT('/')));
                    query += wxT("' AND song_path = '");
                    query += escape_query_str(wxPathOnly(CueItem.m_TrackPath) + wxT("/"));
                    query += wxT("' AND song_offset = 0");
                    ExecuteUpdate(query);

                    guTagInfo *TagInfo = guGetTagInfoHandler(CueItem.m_TrackPath);

                    if (TagInfo)
                    {
                        if (TagInfo->Read())
                        {
                            guTrack CurTrack;
                            //guLogMessage(wxT("TagInfo %i '%s' - Len: %i"), Index, TagInfo->m_TrackName .c_str(), TagInfo->m_Length);

                            CurTrack.m_Path = wxPathOnly(CueItem.m_TrackPath) + wxT("/");
                            CurTrack.m_GenreName = CueItem.m_Genre;
                            CurTrack.m_SongName = CueItem.m_Name;
                            CurTrack.m_ArtistName = CueItem.m_ArtistName;
                            CurTrack.m_AlbumArtist = CueItem.m_AlbumArtist;
                            CurTrack.m_Composer = CueItem.m_Composer;
                            CurTrack.m_Comments = CueItem.m_Comment;
                            CurTrack.m_AlbumName = CueItem.m_AlbumName;
                            CurTrack.m_Offset = CueItem.m_Start;
                            CurTrack.m_Length = CueItem.m_Length;
                            CurTrack.m_FileName = CueItem.m_TrackPath.AfterLast(wxT('/'));
                            CurTrack.m_Number = Index + 1;
                            long Year;
                            if (CueItem.m_Year.ToLong(&Year))
                                CurTrack.m_Year = Year;
                            CurTrack.m_Bitrate = TagInfo->m_Bitrate;

                            CurTrack.m_PathId = GetPathId(CurTrack.m_Path);
                            CurTrack.m_ComposerId = GetComposerId(CurTrack.m_Composer);

                            if (CurTrack.m_ArtistName.IsEmpty())
                                CurTrack.m_ArtistName = _("Unknown");
                            CurTrack.m_ArtistId = GetArtistId(CurTrack.m_ArtistName);

                            CurTrack.m_AlbumArtistId = GetAlbumArtistId(CurTrack.m_AlbumArtist);

                            if (CurTrack.m_AlbumName.IsEmpty() && !CurTrack.m_Path.IsEmpty())
                                CurTrack.m_AlbumName = CurTrack.m_Path.BeforeLast(wxT('/')).AfterLast(wxT('/'));
                            CurTrack.m_AlbumId = GetAlbumId(CurTrack.m_AlbumName, CurTrack.m_ArtistId,
                                                            CurTrack.m_PathId, CurTrack.m_Path);

                            CurTrack.m_CoverId = GetAlbumCoverId(CurTrack.m_AlbumId);

                            if (CurTrack.m_GenreName.IsEmpty())
                                CurTrack.m_GenreName = _("Unknown");
                            CurTrack.m_GenreId = GetGenreId(CurTrack.m_GenreName);

                            if (TagInfo->m_Length)
                                CurTrack.m_FileSize =
                                        (guGetFileSize(CueItem.m_TrackPath) * CueItem.m_Length) / TagInfo->m_Length;
                            else
                                CurTrack.m_FileSize = 0;

                            if (CurTrack.m_SongName.IsEmpty())
                                CurTrack.m_SongName = CurTrack.m_FileName.AfterLast(wxT('/')).BeforeLast(wxT('.'));

                            bool IsNewTrack = false;
                            CurTrack.m_SongId = GetSongId(CurTrack.m_FileName, CurTrack.m_PathId,
                                                          GetFileLastChangeTime(filename), CurTrack.m_Offset,
                                                          &IsNewTrack);

                            UpdateSong(CurTrack, IsNewTrack || allowrating);
                        } else
                            guLogError(
                                wxT("guDbLibrary.ReadFileTags.cue: Cannot read tags from '%s'"), filename.c_str());

                        delete TagInfo;
                    }
                }
            }
            return 1;
        } else
        {
            guTagInfo *TagInfo = guGetTagInfoHandler(filename);
            if (!TagInfo)
                return 0;

            guTrack CurTrack;

            //guLogMessage( wxT( "FileName: '%s'" ), filename.c_str() );
            CurTrack.m_Path = GetPathAddTrailSep(wxPathOnly(filename));

            if (TagInfo->Read())
            {
                CurTrack.m_Composer = TagInfo->m_Composer;
                CurTrack.m_ArtistName = TagInfo->m_ArtistName;
                CurTrack.m_AlbumArtist = TagInfo->m_AlbumArtist;
                CurTrack.m_AlbumName = TagInfo->m_AlbumName;
                CurTrack.m_GenreName = TagInfo->m_GenreName;
                CurTrack.m_SongName = TagInfo->m_TrackName;
                CurTrack.m_Number = TagInfo->m_Track;
                CurTrack.m_Year = TagInfo->m_Year;
                CurTrack.m_Length = TagInfo->m_Length;
                CurTrack.m_Bitrate = TagInfo->m_Bitrate;
                CurTrack.m_Comments = TagInfo->m_Comments;
                CurTrack.m_Disk = TagInfo->m_Disk;
                CurTrack.m_Rating = TagInfo->m_Rating;
                CurTrack.m_PlayCount = TagInfo->m_PlayCount;
            } else
                guLogError(wxT("guDbLibrary.ReadFileTags.tag: Cannot read tags from '%s'"), filename.c_str());

            CurTrack.m_PathId = GetPathId(CurTrack.m_Path);
            CurTrack.m_ComposerId = GetComposerId(CurTrack.m_Composer);

            if (CurTrack.m_ArtistName.IsEmpty())
                CurTrack.m_ArtistName = _("Unknown");
            CurTrack.m_ArtistId = GetArtistId(CurTrack.m_ArtistName);

            CurTrack.m_AlbumArtistId = GetAlbumArtistId(CurTrack.m_AlbumArtist);

            if (CurTrack.m_AlbumName.IsEmpty() && !CurTrack.m_Path.IsEmpty())
                CurTrack.m_AlbumName = CurTrack.m_Path.BeforeLast(wxT('/')).AfterLast(wxT('/'));
            CurTrack.m_AlbumId = GetAlbumId(CurTrack.m_AlbumName, CurTrack.m_ArtistId, CurTrack.m_PathId,
                                            CurTrack.m_Path);

            CurTrack.m_CoverId = GetAlbumCoverId(CurTrack.m_AlbumId);

            if (CurTrack.m_GenreName.IsEmpty())
                CurTrack.m_GenreName = _("Unknown");
            CurTrack.m_GenreId = GetGenreId(CurTrack.m_GenreName);

            CurTrack.m_FileName = filename.AfterLast(wxT('/'));
            CurTrack.m_FileSize = guGetFileSize(filename);

            if (CurTrack.m_SongName.IsEmpty())
                CurTrack.m_SongName = CurTrack.m_FileName.AfterLast(wxT('/')).BeforeLast(wxT('.'));

            bool IsNewTrack = false;
            CurTrack.m_SongId = GetSongId(CurTrack.m_FileName, CurTrack.m_PathId,
                                          GetFileLastChangeTime(filename), CurTrack.m_Offset, &IsNewTrack);

            wxArrayInt ArrayIds;

            if (TagInfo->m_TrackLabels.Count())
            {
                ArrayIds.Add(CurTrack.m_SongId);
                wxArrayInt TrackLabelIds = GetLabelIds(TagInfo->m_TrackLabels);
                SetSongsLabels(ArrayIds, TrackLabelIds);
            }

            if (TagInfo->m_ArtistLabels.Count())
            {
                ArrayIds.Empty();
                ArrayIds.Add(CurTrack.m_ArtistId);
                wxArrayInt ArtistLabelIds = GetLabelIds(TagInfo->m_ArtistLabels);
                SetArtistsLabels(ArrayIds, ArtistLabelIds);
            }

            if (TagInfo->m_AlbumLabels.Count())
            {
                ArrayIds.Empty();
                ArrayIds.Add(CurTrack.m_AlbumId);
                wxArrayInt AlbumLabelIds = GetLabelIds(TagInfo->m_AlbumLabels);
                SetAlbumsLabels(ArrayIds, AlbumLabelIds);
            }

            UpdateSong(CurTrack, IsNewTrack || allowrating);

            delete TagInfo;
            return 1;
        }

        return 0;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::AddFiles(const wxArrayString &files)
    {
        int Count = files.Count();
        for (int Index = 0; Index < Count; Index++)
            ReadFileTags(files[Index]);
        return Count;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateTrackLength(const int trackid, const int length)
    {
        wxString query = wxString::Format(wxT("UPDATE songs SET song_length = %i "
            "WHERE song_id = %i"), length, trackid);
        ExecuteUpdate(query);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateTrackBitRate(const int trackid, const int bitrate)
    {
        wxString query = wxString::Format(wxT("UPDATE songs SET song_bitrate = %i "
            "WHERE song_id = %i"), bitrate, trackid);
        ExecuteUpdate(query);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateSongs(const guTrackArray *Songs, const wxArrayInt &changedflags)
    {
        guTrack *Song;
        int count = Songs->Count();
        wxArrayPtrVoid MediaViewerPtrs;

        // Process each Track
        for (int index = 0; index < count; index++)
        {
            if (!(changedflags[index] & (guTRACK_CHANGED_DATA_TAGS | guTRACK_CHANGED_DATA_RATING)))
                continue;

            Song = &(*Songs)[index];

            if (Song->m_Type == guTRACK_TYPE_DB)
            {
                if (Song->m_MediaViewer)
                {
                    if (MediaViewerPtrs.Index(Song->m_MediaViewer) == wxNOT_FOUND)
                        MediaViewerPtrs.Add(Song->m_MediaViewer);
                }

                //
                // Update the Library
                //
                wxString PathName;
                int PathId;
                int AlbumArtistId;
                int ArtistId;
                int ComposerId;
                int AlbumId;
                int GenreId;

                PathName = wxPathOnly(Song->m_FileName) + wxT('/');
                //wxSetWorkingDirectory( PathName );
                PathId = GetPathId(PathName);
                AlbumArtistId = GetAlbumArtistId(Song->m_AlbumArtist);
                ArtistId = GetArtistId(Song->m_ArtistName);
                ComposerId = GetComposerId(Song->m_Composer);
                AlbumId = GetAlbumId(Song->m_AlbumName, ArtistId, PathId, PathName, Song->m_CoverId);
                GenreId = GetGenreId(Song->m_GenreName);

                //FileName = Song->FileName.AfterLast( '/' );
                //printf( ( wxT( "FileName: " ) + CurSong.FileName ).ToAscii() ); printf( "\n" );
                //GetSongId( &SongId, FileName, PathId );

                guTrack CurTrack = *Song;
                CurTrack.m_GenreId = GenreId;
                CurTrack.m_ArtistId = ArtistId;
                CurTrack.m_AlbumArtistId = AlbumArtistId;
                CurTrack.m_ComposerId = ComposerId;
                CurTrack.m_AlbumId = AlbumId;
                CurTrack.m_PathId = PathId;
                CurTrack.m_FileName = Song->m_FileName.AfterLast('/');
                //CurSong.SongId = SongId;
                //CurSong.FileName.c_str(),
                //CurSong.Number
                //CurSong.Year,
                //CurSong.Length,
                UpdateSong(CurTrack, true);
            }
        }

        // We added in PanelPtr all panels we are updating tracks
        // And send the clean db event to all of them
        wxCommandEvent event(wxEVT_MENU, ID_LIBRARY_DOCLEANDB);
        count = MediaViewerPtrs.Count();
        for (int index = 0; index < count; index++)
        {
            //    event.SetClientData( ( void * ) MediaViewerPtrs[ index ] );
            wxPostEvent((guMediaViewer *) MediaViewerPtrs[index], event);
        }
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::UpdateSong(const guTrack &track, const bool allowrating)
    {
        wxString query;

        query = wxString::Format(wxT("UPDATE songs SET song_name = '%s', "
            "song_genreid = %u, song_genre = '%s', "
            "song_artistid = %u, song_artist = '%s', "
            "song_albumartistid = %u, song_albumartist = '%s', "
            "song_albumid = %u, song_album = '%s', "
            "song_pathid = %u, song_path = '%s', "
            "song_filename = '%s', song_format = '%s', "
            "song_number = %u, song_year = %u, "
            "song_composerid = %u, song_composer = '%s', "
            "song_comment = '%s', song_coverid = %i, song_disk = '%s', "
            "song_length = %u, song_offset = %u, song_bitrate = %u, "
            "song_filesize = %u"),
                                 escape_query_str(track.m_SongName).c_str(),
                                 track.m_GenreId,
                                 escape_query_str(track.m_GenreName).c_str(),
                                 track.m_ArtistId,
                                 escape_query_str(track.m_ArtistName).c_str(),
                                 track.m_AlbumArtistId,
                                 escape_query_str(track.m_AlbumArtist).c_str(),
                                 track.m_AlbumId,
                                 escape_query_str(track.m_AlbumName).c_str(),
                                 track.m_PathId,
                                 escape_query_str(track.m_Path).c_str(),
                                 escape_query_str(track.m_FileName).c_str(),
                                 escape_query_str(track.m_FileName.AfterLast('.')).c_str(),
                                 track.m_Number,
                                 track.m_Year,
                                 track.m_ComposerId, //escape_query_str( track.m_Composer ).c_str(),
                                 escape_query_str(track.m_Composer).c_str(),
                                 escape_query_str(track.m_Comments).c_str(),
                                 track.m_CoverId,
                                 escape_query_str(track.m_Disk).c_str(),
                                 track.m_Length,
                                 track.m_Offset,
                                 track.m_Bitrate,
                                 track.m_FileSize);

        if (allowrating)
        {
            query += wxString::Format(wxT(", song_rating = %i, song_playcount = %i "),
                                      track.m_Rating,
                                      track.m_PlayCount);
        }

        query += wxString::Format(wxT(" WHERE song_id = %u;"), track.m_SongId);

        return ExecuteUpdate(query);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateImageFile(const char *filename, const char *saveto, const wxBitmapType type,
                                      const int maxsize)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxString FileName;
        int AlbumId;
        int CoverId;
        wxString CoverHash;
        wxString CoverFile = FileName = wxString(filename, wxConvUTF8);
        wxString SaveTo = wxString(saveto, wxConvUTF8);

        if (FileName.IsEmpty())
            return;

        if (guIsValidAudioFile(FileName))
        {
            //guLogMessage( wxT( "Trying to get image from file '%s'" ), FileName.c_str() );
            wxImage *Image = guTagGetPicture(FileName);
            if (Image)
            {
                if (!Image->IsOk())
                {
                    delete Image;
                    return;
                }

                if (maxsize)
                    Image->Rescale(maxsize, maxsize, wxIMAGE_QUALITY_HIGH);

                CoverFile = wxPathOnly(FileName) + wxT('/') + SaveTo;
                if (!Image->SaveFile(CoverFile, type))
                {
                    delete Image;
                    return;
                }
                FileName = CoverFile;
                delete Image;
            } else
                return;
        }


        guMD5 md5;
        wxString NewCoverHash = md5.MD5File(FileName);

        escape_query_str(&CoverFile);
        query = wxT("SELECT cover_id, cover_hash FROM covers WHERE cover_path = '") + CoverFile +
                wxT("' LIMIT 1;");
        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            CoverId = dbRes.GetInt(0);
            CoverHash = dbRes.GetString(1);
            dbRes.Finalize();

            if (NewCoverHash != CoverHash)
                UpdateCoverFile(CoverId, FileName, NewCoverHash);

            return;
        }
        dbRes.Finalize();

        // No Cover was found with this PathName
        wxString Path = wxPathOnly(FileName) + wxT('/');

        escape_query_str(&Path);

        // Now try to search the album in this path
        query = wxString::Format(wxT("SELECT song_albumid FROM songs WHERE song_path = '%s' LIMIT 1"), Path.c_str());
        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            AlbumId = dbRes.GetInt(0);
            dbRes.Finalize();

            SetAlbumCover(AlbumId, FileName, NewCoverHash);

            return;
        }
        //An image which appear to not be to any album was found. We do nothing with this
        //  else
        //  {
        //      guLogError( wxT( "The image '%s' with no album set" ), FileName.c_str() );
        //  }
        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetFiltersCount() const
    {
        return m_TeFilters.Count() || m_GeFilters.Count() || m_LaFilters.Count() || m_ArFilters.Count() ||
               m_AAFilters.Count() || m_CoFilters.Count() || m_AlFilters.Count() || m_YeFilters.Count() ||
               m_RaFilters.Count() || m_PcFilters.Count() || (m_DiFilters.IsEmpty() ? 0 : 1);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetTeFilters(const wxArrayString &NewTeFilters, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetTeFilters %i" ), NewTeFilters.Count() );
        m_TeFilters = NewTeFilters;
        if (locked)
            return;
        m_LaFilters.Empty();
        m_GeFilters.Empty();
        m_AAFilters.Empty();
        m_ArFilters.Empty();
        m_CoFilters.Empty();
        m_AlFilters.Empty();
        m_YeFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetLaFilters(const wxArrayInt &NewLaFilters, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetTaFilters %i" ), NewTaFilters.Count() );
        if (NewLaFilters.Index(0) != wxNOT_FOUND)
            m_LaFilters.Empty();
        else
            m_LaFilters = NewLaFilters;

        if (locked)
            return;
        m_GeFilters.Empty();
        m_AAFilters.Empty();
        m_ArFilters.Empty();
        m_CoFilters.Empty();
        m_AlFilters.Empty();
        m_YeFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetDiFilters(const wxString &NewDiFilters, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetDiFilters %i" ), NewDiFilters.Count() );
        if (NewDiFilters == wxEmptyString || NewDiFilters == wxT(GU_COLLECTION_DUMMY_ROOTDIR))
            m_DiFilters = wxEmptyString;
        else
            m_DiFilters = NewDiFilters;

        if (locked)
            return;
        m_GeFilters.Empty();
        m_AAFilters.Empty();
        m_ArFilters.Empty();
        m_CoFilters.Empty();
        m_AlFilters.Empty();
        m_YeFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetGeFilters(const wxArrayInt &NewGeFilters, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetGeFilters %i" ), NewGeFilters.Count() );
        if (!DirectoryHasPath() && NewGeFilters.Index(0) != wxNOT_FOUND)
            m_GeFilters.Empty();
        else
            m_GeFilters = NewGeFilters;

        if (locked)
            return;
        m_AAFilters.Empty();
        m_ArFilters.Empty();
        m_CoFilters.Empty();
        m_AlFilters.Empty();
        m_YeFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetArFilters(const wxArrayInt &NewArFilters, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetArFilters %i" ), NewArFilters.Count() );
        if (NewArFilters.Index(0) != wxNOT_FOUND)
            m_ArFilters.Empty();
        else
            m_ArFilters = NewArFilters;

        if (locked)
            return;
        m_YeFilters.Empty();
        m_AlFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetCoFilters(const wxArrayInt &filter, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetCoFilters %i" ), filter.Count() );
        if (filter.Index(0) != wxNOT_FOUND)
            m_CoFilters.Empty();
        else
            m_CoFilters = filter;

        if (locked)
            return;
        m_AAFilters.Empty();
        m_ArFilters.Empty();
        m_YeFilters.Empty();
        m_AlFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetAAFilters(const wxArrayInt &filter, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetAAFilters %i" ), filter.Count() );
        if (filter.Index(0) != wxNOT_FOUND)
            m_AAFilters.Empty();
        else
            m_AAFilters = filter;

        if (locked)
            return;
        m_ArFilters.Empty();
        m_YeFilters.Empty();
        m_AlFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetYeFilters(const wxArrayInt &filter, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetYeFilters %i" ), filter.Count() );
        if (filter.Index(0) != wxNOT_FOUND)
            m_YeFilters.Empty();
        else
            m_YeFilters = filter;

        if (locked)
            return;
        m_AlFilters.Empty();
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetAlFilters(const wxArrayInt &NewAlFilters, const bool locked)
    {
        //guLogMessage( wxT( "guDbLibrary::SetAlFilters %i" ), NewAlFilters.Count() );
        //    if( locked )
        //        return;
        if (NewAlFilters.Index(0) != wxNOT_FOUND)
            m_AlFilters.Empty();
        else
            m_AlFilters = NewAlFilters;

        if (locked)
            return;
        m_RaFilters.Empty();
        m_PcFilters.Empty();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetRaFilters(const wxArrayInt &filter)
    {
        //guLogMessage( wxT( "guDbLibrary::SetYeFilters %i" ), filter.Count() );
        if (filter.Index(0) != wxNOT_FOUND)
            m_RaFilters.Empty();
        else
        {
            //m_RaFilters = filter;
            m_RaFilters.Empty();
            int Count = filter.Count();
            for (int Index = 0; Index < Count; Index++)
                m_RaFilters.Add(filter[Index] - 1);
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetPcFilters(const wxArrayInt &filter)
    {
        //guLogMessage( wxT( "guDbLibrary::SetYeFilters %i" ), filter.Count() );
        if (filter.Index(0) != wxNOT_FOUND)
            m_PcFilters.Empty();
        else
        {
            m_PcFilters.Empty();
            int Count = filter.Count();
            for (int Index = 0; Index < Count; Index++)
                m_PcFilters.Add(filter[Index] - 1);
        }
    }

    // -------------------------------------------------------------------------------- //
    wxString guDbLibrary::FiltersSQL(int Level)
    {
        wxString subquery;
        wxString query;
        wxString RetVal = wxEmptyString;

        if (DirectoryHasPath())
            RetVal += DirectoryFilterToSQL(m_DiFilters);

        if (m_TeFilters.Count())
        {
            if (!RetVal.IsEmpty())
                RetVal += wxT(" AND ");
            RetVal += TextFilterToSQL(m_TeFilters);
        }

        if (Level > guLIBRARY_FILTER_LABELS)
        {
            if (m_LaFilters.Count())
            {
                if (!RetVal.IsEmpty())
                    RetVal += wxT(" AND ");
                RetVal += LabelFilterToSQL(m_LaFilters);
            }

            if (Level > guLIBRARY_FILTER_GENRES)
            {
                if (m_GeFilters.Count())
                {
                    if (!RetVal.IsEmpty())
                        RetVal += wxT(" AND ");
                    RetVal += ArrayToFilter(m_GeFilters, wxT("song_genreid"));
                }

                if (Level > guLIBRARY_FILTER_COMPOSERS)
                {
                    if (m_CoFilters.Count())
                    {
                        if (!RetVal.IsEmpty())
                            RetVal += wxT(" AND ");
                        RetVal += ArrayToFilter(m_CoFilters, wxT("song_composerid"));
                    }

                    if (Level > guLIBRARY_FILTER_ALBUMARTISTS)
                    {
                        if (m_AAFilters.Count())
                        {
                            if (!RetVal.IsEmpty())
                                RetVal += wxT(" AND ");
                            RetVal += ArrayToFilter(m_AAFilters, wxT("song_albumartistid"));
                        }

                        if (Level > guLIBRARY_FILTER_ARTISTS)
                        {
                            if (m_ArFilters.Count())
                            {
                                if (!RetVal.IsEmpty())
                                    RetVal += wxT(" AND ");
                                RetVal += ArrayToFilter(m_ArFilters, wxT("song_artistid"));
                            }
                            if (Level > guLIBRARY_FILTER_YEARS)
                            {
                                if (m_YeFilters.Count())
                                {
                                    if (!RetVal.IsEmpty())
                                        RetVal += wxT(" AND ");
                                    RetVal += ArrayToFilter(m_YeFilters, wxT("song_year"));
                                }

                                if (Level > guLIBRARY_FILTER_ALBUMS)
                                {
                                    if (m_AlFilters.Count())
                                    {
                                        if (!RetVal.IsEmpty())
                                            RetVal += wxT(" AND ");
                                        RetVal += ArrayToFilter(m_AlFilters, wxT("song_albumid"));
                                    }

                                    if (Level >= guLIBRARY_FILTER_SONGS)
                                    {
                                        if (m_RaFilters.Count())
                                        {
                                            if (!RetVal.IsEmpty())
                                                RetVal += wxT(" AND ");
                                            RetVal += ArrayToFilter(m_RaFilters, wxT("song_rating"));
                                        }
                                        if (m_PcFilters.Count())
                                        {
                                            if (!RetVal.IsEmpty())
                                                RetVal += wxT(" AND ");
                                            RetVal += ArrayToFilter(m_PcFilters, wxT("song_playcount"));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        //guLogMessage( wxT( "FilterSQL: '%s'" ), RetVal.c_str() );
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::AddLabel(wxString LabelName)
    {
        wxString query;
        escape_query_str(&LabelName);

        query = wxT("INSERT INTO tags( tag_id, tag_name ) VALUES(NULL, '") +
                LabelName + wxT("');");

        if (ExecuteUpdate(query) == 1)
            return GetLastRowId();

        return 0;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::SetLabelName(const int labelid, const wxString &oldlabel, const wxString &newlabel)
    {
        wxString query;

        if (m_MediaViewer->GetEmbeddMetadata())
        {
            guTrackArray Tracks;
            wxArrayInt Labels;
            Labels.Add(labelid);

            GetLabelsSongs(Labels, &Tracks);
            UpdateSongsLabel(Tracks, oldlabel, newlabel);
        }


        wxString LabelName = newlabel;
        escape_query_str(&LabelName);

        query = query.Format(wxT("UPDATE tags SET tag_name = '%s' WHERE tag_id = %u;"), LabelName.c_str(), labelid);

        if (ExecuteUpdate(query) == 1)
            return GetLastRowId();

        return 0;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::DelLabel(const int LabelId)
    {
        wxString query;

        if (m_MediaViewer->GetEmbeddMetadata())
        {
            guListItems LaItems;
            guTrackArray Tracks;
            wxArrayInt Labels;
            Labels.Add(LabelId);

            GetLabels(&LaItems, true);

            GetLabelsSongs(Labels, &Tracks);
            UpdateSongsLabel(Tracks, guListItemsGetName(LaItems, LabelId), wxEmptyString);
        }

        query = query.Format(wxT("DELETE FROM tags WHERE tag_id = %u;"), LabelId);
        if (ExecuteUpdate(query))
        {
            query = query.Format(wxT("DELETE FROM settags WHERE settag_tagid = %u;"), LabelId);
            if (ExecuteUpdate(query))
                return 1;
        }
        return 0;
    }

    // -------------------------------------------------------------------------------- //
    wxArrayInt guDbLibrary::GetLabels()
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxArrayInt RetVal;

        query = wxT("SELECT tag_id FROM tags ORDER BY tag_id;");
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            RetVal.Add(dbRes.GetInt(0));

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetLabels(guListItems *Labels, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query;
        //  guListItems RetVal;
        //guLogMessage( wxT( "guDbLibrary::GetLabels" ) );

        query = wxT("SELECT tag_id, tag_name FROM tags ORDER BY ");
        query += FullList ? wxT("tag_id;") : wxT("tag_name;");
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            Labels->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetGenres(guListItems *Genres, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, group_by, order_by;
        //guLogMessage( wxT( "guDbLibrary::GetGenres" ) );

        //if( !GetFiltersCount() )
        query = wxT("SELECT song_genreid, song_genre FROM songs ");
        group_by = " GROUP BY song_genreid ";
        order_by = wxEmptyString;

        if (!FullList)
        {
            order_by = wxT("ORDER BY song_genre");
            if (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count())
                query += wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_GENRES);
        }
        query += (group_by + order_by);
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            Genres->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetArtists(guListItems *Artists, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, group_by, order_by;
        //guLogMessage( wxT( "guDbLibrary::GetArtists" ) );

        query = wxT("SELECT song_artistid, song_artist FROM songs ");
        group_by = " GROUP BY song_artistid ";
        order_by = wxEmptyString;

        if (!FullList)
        {
            order_by += wxT(" ORDER BY song_artist");
            if (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() || m_AAFilters.
                Count() || m_CoFilters.Count())
                query += wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_ARTISTS);
        }
        query += (group_by + order_by);
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            Artists->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetYears(guListItems *Years, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, order_by;

        query = wxT("SELECT DISTINCT song_year FROM songs WHERE song_year > 0 ");
        order_by = wxT(" ORDER BY song_year DESC;");

        if (!FullList && (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() ||
                          m_AAFilters.Count() || m_ArFilters.Count() || m_CoFilters.Count()))
            query += (wxT("AND ") + FiltersSQL(guLIBRARY_FILTER_YEARS));

        query += order_by;
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            int Year = dbRes.GetInt(0);
            Years->Add(new guListItem(Year, wxString::Format(wxT("%i"), Year)));
        }
        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetRatings(guListItems *Ratings, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, order_by;

        query = wxT("SELECT DISTINCT song_rating FROM songs WHERE song_rating >= 0 ");
        order_by = wxT("ORDER BY song_rating DESC;");

        if (!FullList && (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() ||
                          m_AAFilters.Count() || m_ArFilters.Count() || m_CoFilters.Count() || m_YeFilters.Count()))
            query += (wxT("AND ") + FiltersSQL(guLIBRARY_FILTER_RATINGS));

        query += order_by;
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            int Rating = dbRes.GetInt(0);

            // To avoid using the 0 as 0 is used for All
            Ratings->Add(new guListItem(Rating + 1, wxString::Format(wxT("%i"), Rating)));
        }
        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetPlayCounts(guListItems *PlayCounts, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, order_by;

        query = wxT("SELECT DISTINCT song_playcount FROM songs ");
        order_by = "ORDER BY song_playcount;";

        if (!FullList && (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() ||
                          m_AAFilters.Count() || m_ArFilters.Count() || m_CoFilters.Count() || m_YeFilters.Count()))
            query += (wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_PLAYCOUNTS));

        query += order_by;
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            int PlayCount = dbRes.GetInt(0);
            // To avoid using the 0 as 0 is used for All
            PlayCounts->Add(new guListItem(PlayCount + 1, wxString::Format(wxT("%i"), PlayCount)));
        }
        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetAlbumArtists(guListItems *Items, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, group_by, order_by;

        query = wxT("SELECT song_albumartistid, song_albumartist FROM songs ");
        group_by = " GROUP BY song_albumartistid ";
        order_by = wxEmptyString;

        if (!FullList)
        {
            query += wxT("WHERE song_albumartist > '' ");
            order_by = wxT("ORDER BY song_albumartist");

            if (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() || m_CoFilters.
                Count())
                query += wxT(" AND ") + FiltersSQL(guLIBRARY_FILTER_ALBUMARTISTS);
        }
        query += (group_by + order_by);
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            Items->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetComposers(guListItems *Items, const bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, group_by, order_by;

        query = wxT("SELECT song_composerid, song_composer FROM songs ");
        group_by = " GROUP BY song_composerid ";
        order_by = wxEmptyString;

        if (!FullList)
        {
            query += wxT("WHERE song_composer > '' ");
            order_by = wxT("ORDER BY song_composer");

            if (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count())
                query += wxT("AND ") + FiltersSQL(guLIBRARY_FILTER_COMPOSERS);
        }
        query += (group_by + order_by);
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            Items->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetPaths(guListItems *Paths, bool FullList)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxString CoverPath;
        //guLogMessage( wxT( "guDbLibrary::GetPaths" ) );

        query = wxT("SELECT DISTINCT song_pathid, song_path FROM songs GROUP BY song_pathid");
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
            Paths->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetAlbumsOrder(const int order)
    {
        m_AlbumsOrder = order;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetAlbums(guAlbumItems *Albums, bool FullList)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        //guLogMessage( wxT( "guDbLibrary::GetAlbums" )
        query = wxT("SELECT song_albumid, song_album, song_artistid, song_artist, song_albumartist, "
            "song_coverid, MAX(song_year) FROM songs ");

        if (FullList)
            query += wxT("GROUP BY song_albumid ");
        else
        {
            if (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() ||
                m_AAFilters.Count() || m_ArFilters.Count() || m_CoFilters.Count() || m_YeFilters.Count())
            {
                query += wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_ALBUMS);
            }

            query += wxT(" GROUP BY song_albumid ");
            query += wxT(" ORDER BY ");

            switch (m_AlbumsOrder)
            {
                case guALBUMS_ORDER_NAME:
                    query += wxT("song_album, song_disk");
                    break;

                case guALBUMS_ORDER_YEAR:
                    query += wxT("song_year, song_album, song_disk");
                    break;

                case guALBUMS_ORDER_YEAR_REVERSE:
                    query += wxT("song_year DESC, song_album, song_disk");
                    break;

                case guALBUMS_ORDER_ARTIST_NAME:
                    query += wxT(
                        "coalesce(nullif(song_albumartist,''),song_artist) COLLATE NOCASE, song_album, song_disk ");
                    break;

                case guALBUMS_ORDER_ARTIST_YEAR:
                    query += wxT(
                        "coalesce(nullif(song_albumartist,''),song_artist) COLLATE NOCASE, song_year, song_album, song_disk");
                    break;

                case guALBUMS_ORDER_ARTIST_YEAR_REVERSE:
                default:
                    query += wxT(
                        "coalesce(nullif(song_albumartist,''),song_artist) COLLATE NOCASE, song_year DESC, song_album, song_disk");
                    break;
            }
        }

        //guLogMessage( wxT( "GetAlbums: %s" ), query.c_str() );
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            //query = wxT( "SELECT song_albumid, song_album, song_artistid, song_artist, song_albumartist, song_coverid, MAX(song_year) FROM songs " );
            guAlbumItem *AlbumItem = new guAlbumItem();
            AlbumItem->m_Id = dbRes.GetInt(0);
            AlbumItem->m_Name = dbRes.GetString(1);
            AlbumItem->m_ArtistId = dbRes.GetInt(2);
            AlbumItem->m_ArtistName = dbRes.GetString(4);
            if (AlbumItem->m_ArtistName.IsEmpty())
                AlbumItem->m_ArtistName = dbRes.GetString(3);
            AlbumItem->m_CoverId = dbRes.GetInt(5);
            AlbumItem->m_Year = dbRes.GetInt(6);
            AlbumItem->m_Thumb = GetCoverBitmap(AlbumItem->m_CoverId);
            Albums->Add(AlbumItem);
        }

        dbRes.Finalize();
        //  return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetAlbums(guListItems *Albums, bool FullList)
    {
        wxSQLite3ResultSet dbRes;
        wxString query, group_by;

        //guLogMessage( wxT( "guDbLibrary::GetAlbums" )
        query = wxT("SELECT song_albumid, song_album FROM songs ");
        group_by = " GROUP BY song_albumid;";

        if (!FullList)
        {
            if (DirectoryHasPath() || m_TeFilters.Count() || m_LaFilters.Count() || m_GeFilters.Count() ||
                m_AAFilters.Count() || m_ArFilters.Count() || m_CoFilters.Count() || m_YeFilters.Count())
                query += wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_ALBUMS);
        }
        query += group_by;

        //guLogMessage( wxT( "GetAlbums: %s" ), query.c_str() );
        dbRes = (const wxSQLite3ResultSet) ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            guListItem *AlbumItem = new guListItem(dbRes.GetInt(0), dbRes.GetString(1));
            Albums->Add(AlbumItem);
        }
        dbRes.Finalize();
    }

    const wxString DynPlayListToSQLQuery(guDynPlayList *playlist);

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::GetAlbumDetails(const int albumid, int *year, int *trackcount)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        bool RetVal = false;
        query = wxString::Format(
            wxT("SELECT MAX(song_year), COUNT(song_id) FROM songs WHERE song_albumid = %i;"), albumid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            *year = dbRes.GetInt(0);
            *trackcount = dbRes.GetInt(1);
            RetVal = true;
        }

        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumYear(const int albumid)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;
        query = wxString::Format(wxT("SELECT MAX(song_year) FROM songs WHERE song_albumid = %i;"), albumid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetInt(0);

        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumTrackCount(const int albumid)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;
        query = wxString::Format(wxT("SELECT COUNT( song_id ) FROM songs WHERE song_albumid = %i"), albumid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetInt(0);

        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumsCount(guDynPlayList *filter, const wxArrayString &textfilters)
    {
        wxString query;
        wxString postquery;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;

        query = wxT("SELECT COUNT( DISTINCT song_albumid ) FROM songs ");

        if (filter)
            postquery += DynPlayListToSQLQuery(filter);

        if (textfilters.Count())
        {
            if (postquery.IsEmpty())
                postquery = wxT("WHERE ");
            else
                postquery += wxT(" AND ");
            //postquery += wxT( "song_albumid = album_id AND song_artistid = artist_id AND " );
            postquery += AlbumBrowserTextFilterToSQL(textfilters);
        }

        dbRes = ExecuteQuery(query + postquery);

        if (dbRes.NextRow())
        {
            RetVal = dbRes.GetInt(0);
            guLogMessage(wxT("Got %i albums"), RetVal);
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbums(guAlbumBrowserItemArray *items, guDynPlayList *filter,
                               const wxArrayString &textfilters, const int start, const int count, const int order)
    {
        if (!count)
            return 0;

        wxString query;
        wxSQLite3ResultSet dbRes;
        wxString subquery;

        query = wxT(
            "SELECT song_albumid, song_album, song_artistid, song_albumartist, song_artist, song_coverid FROM songs ");
        if (filter)
        {
            subquery = DynPlayListToSQLQuery(filter);
        }

        if (textfilters.Count())
        {
            subquery += subquery.IsEmpty() ? wxT("WHERE ") : wxT(" AND ");
            subquery += AlbumBrowserTextFilterToSQL(textfilters);
        }

        query += subquery + wxT(" GROUP BY song_albumid ORDER BY ");

        switch (order)
        {
            case guALBUMS_ORDER_NAME:
                query += wxT("song_album");
                break;

            case guALBUMS_ORDER_YEAR:
                query += wxT("song_year");
                break;

            case guALBUMS_ORDER_YEAR_REVERSE:
                query += wxT("song_year DESC");
                break;

            case guALBUMS_ORDER_ARTIST_NAME:
                query += wxT("coalesce(nullif(song_albumartist, ''),song_artist) COLLATE NOCASE, song_album ");
                break;

            case guALBUMS_ORDER_ARTIST_YEAR:
                query += wxT("coalesce(nullif(song_albumartist,''),song_artist) COLLATE NOCASE, song_year");
                break;

            case guALBUMS_ORDER_ADDEDTIME:
                query += wxT("song_addedtime DESC,song_album,song_disk ");
                break;

            case guALBUMS_ORDER_ARTIST_YEAR_REVERSE:
            default:
                query += wxT("coalesce(nullif(song_albumartist,''),song_artist) COLLATE NOCASE, song_year DESC");
                break;
        }

        query += wxString::Format(wxT(" LIMIT %i, %i"), start, count);

        //guLogMessage( wxT( "GetAlbums:\n%s" ), query.c_str() );

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            guAlbumBrowserItem *Item = new guAlbumBrowserItem();
            Item->m_AlbumId = dbRes.GetInt(0); //AlbumId;
            Item->m_AlbumName = dbRes.GetString(1);
            Item->m_ArtistId = dbRes.GetInt(2);
            Item->m_ArtistName = dbRes.GetString(3);
            if (Item->m_ArtistName.IsEmpty())
                Item->m_ArtistName = dbRes.GetString(4);
            Item->m_CoverId = dbRes.GetInt(5);
            Item->m_CoverBitmap = GetCoverBitmap(Item->m_CoverId, false);
            Item->m_Year = 0; //dbRes.GetInt( 4 );
            Item->m_TrackCount = 0; //dbRes.GetInt( 5 );
            items->Add(Item);
        }
        dbRes.Finalize();

        return items->Count();
    }

    // -------------------------------------------------------------------------------- //
    wxArrayString guDbLibrary::GetAlbumsPaths(const wxArrayInt &AlbumIds)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxArrayString RetVal;

        query = wxT("SELECT DISTINCT song_path FROM songs WHERE song_albumid IN ") + ArrayIntToStrList(AlbumIds);

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            RetVal.Add(dbRes.GetString(0));
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetStaticPlayList(const wxString &path)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int PLId = wxNOT_FOUND;

        query = wxString::Format(
            wxT("SELECT playlist_id FROM playlists WHERE playlist_path = '%s' AND playlist_type = 0 LIMIT 1"),
            escape_query_str(path).c_str());

        dbRes = (const wxSQLite3ResultSet) ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            PLId = dbRes.GetInt(0);
        }
        dbRes.Finalize();
        return PLId;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::CreateStaticPlayList(const wxString &path, const wxArrayInt &songs)
    {
        int PlayListId = 0;
        wxString query;

        wxString Name;
        wxString Path = path;
        if (guPlaylistFile::IsValidPlayList(path))
            Name = wxFileNameFromPath(path).BeforeLast(wxT('.'));
        else
        {
            Name = path;
            Path = wxEmptyString;
        }

        query = wxString::Format(
            wxT(
                "INSERT INTO playlists( playlist_id, playlist_name, playlist_type, playlist_path ) VALUES(NULL, '%s', 0, '%s' );"),
            escape_query_str(Name).c_str(),
            escape_query_str(Path).c_str());

        if (ExecuteUpdate(query))
            PlayListId = GetLastRowId();

        if (PlayListId)
        {
            int count = songs.Count();
            for (int index = 0; index < count; index++)
            {
                //      query.Add( wxT( "CREATE TABLE IF NOT EXISTS playlists( playlist_id INTEGER PRIMARY KEY AUTOINCREMENT, playlist_name varchar(100), "
                //                      "playlist_type INTEGER(2));" ) );
                //      query.Add( wxT( "CREATE TABLE IF NOT EXISTS plsets(plset_id INTEGER PRIMARY KEY AUTOINCREMENT, plset_plid INTEGER, plset_songid INTEGER, "
                //                      "plset_type INTEGER(2), plset_option INTEGER(2), plset_text TEXT(255), plset_option2 INTEGER );" ) );
                query = wxString::Format(wxT(
                    "INSERT INTO plsets(plset_id, plset_plid, plset_songid, plset_type, plset_option, plset_text, plset_option2 ) "
                    "VALUES(NULL, %u, %u, 0, 0, '', 0 );"),
                                         PlayListId,
                                         songs[index]);
                ExecuteUpdate(query);
                //dbRes.Finalize();
            }
        }
        return PlayListId;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::UpdateStaticPlayList(const int plid, const wxArrayInt &tracks)
    {
        wxString query;

        query = wxString::Format(wxT("DELETE FROM plsets WHERE plset_plid = %u;"), plid);
        ExecuteUpdate(query);

        return AppendStaticPlayList(plid, tracks);
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::AppendStaticPlayList(const int plid, const wxArrayInt &tracks)
    {
        wxString query;

        int count = tracks.Count();
        for (int index = 0; index < count; index++)
        {
            query = wxString::Format(wxT(
                "INSERT INTO plsets(plset_id, plset_plid, plset_songid, plset_type, plset_option, plset_text, plset_option2 ) "
                "VALUES(NULL, %u, %u, 0, 0, '', 0 );"),
                                     plid,
                                     tracks[index]);
            ExecuteUpdate(query);
        }
        return tracks.Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::DelPlaylistSetIds(const int plid, const wxArrayInt &setids)
    {
        wxString query;
        query = wxString::Format(wxT("DELETE FROM plsets WHERE plset_plid = %u AND "), plid);
        query += ArrayToFilter(setids, wxT("plset_id"));

        //guLogMessage( wxT( "DelPlayListSetIds:\n%s" ), query.c_str() );

        int RetVal = ExecuteUpdate(query);
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListFiles(const int plid, wxFileDataObject *files)
    {
        int Count = 0;
        wxString query;
        wxSQLite3ResultSet dbRes;
        query = wxT("SELECT song_filename, song_path FROM plsets, songs "
            "WHERE plset_songid = song_id AND plset_plid = ");
        query += wxString::Format(wxT("%u"), plid);

        //guLogMessage( wxT( "GetPlayListFiles:\n%s" ), query.c_str() );
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            files->AddFile(dbRes.GetString(1) + dbRes.GetString(0));
            Count++;
        }

        dbRes.Finalize();
        return Count;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateStaticPlayListFile(const int plid)
    {
        wxString PlaylistPath = GetPlayListPath(plid);
        if (!PlaylistPath.IsEmpty())
        {
            wxFileName FileName(PlaylistPath);
            if (FileName.Normalize(
                wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_CASE | wxPATH_NORM_ABSOLUTE | wxPATH_NORM_LONG |
                wxPATH_NORM_SHORTCUT | wxPATH_NORM_ENV_VARS))
            {
                guTrackArray Tracks;
                GetPlayListSongs(plid, guPLAYLIST_TYPE_STATIC, &Tracks, nullptr, nullptr);
                guPlaylistFile PlayListFile;
                PlayListFile.SetName(FileName.GetFullPath());
                int Count = Tracks.Count();
                for (int Index = 0; Index < Count; Index++)
                {
                    PlayListFile.AddItem(Tracks[Index].m_FileName,
                                         Tracks[Index].m_ArtistName + wxT(" - ") + Tracks[Index].m_SongName,
                                         Tracks[Index].m_Length / 1000);
                }

                PlayListFile.Save(FileName.GetFullPath());
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::CreateDynamicPlayList(const wxString &name, const guDynPlayList *playlist)
    {
        int PlayListId = 0;
        wxString query;
        wxSQLite3ResultSet dbRes;

        //  playlists( playlist_id INTEGER PRIMARY KEY AUTOINCREMENT, playlist_name varchar(100), "
        //            "playlist_type INTEGER(2), playlist_limited BOOLEAN, playlist_limitvalue INTEGER, playlist_limittype INTEGER(2), "
        //            "playlist_sorted BOOLEAN, playlist_sorttype INTEGER(2), playlist_sortdesc BOOLEAN, playlist_anyoption BOOLEAN);" ) );

        query = wxT("INSERT INTO playlists( playlist_id, playlist_name, playlist_type, "
            "playlist_limited, playlist_limitvalue, playlist_limittype, "
            "playlist_sorted, playlist_sorttype, playlist_sortdesc, "
            "playlist_anyoption ) ");
        query += wxString::Format(wxT("VALUES(NULL, '%s', %u, %u, %u, %u, %u, %u, %u, %u );"),
                                  escape_query_str(name).c_str(),
                                  guPLAYLIST_TYPE_DYNAMIC,
                                  playlist->m_Limited, playlist->m_LimitValue, playlist->m_LimitType,
                                  playlist->m_Sorted, playlist->m_SortType, playlist->m_SortDesc,
                                  playlist->m_AnyOption);

        if (ExecuteUpdate(query) == 1)
            PlayListId = GetLastRowId();

        if (PlayListId)
        {
            int count = playlist->m_Filters.Count();
            for (int index = 0; index < count; index++)
            {
                guFilterItem *FilterItem = &playlist->m_Filters[index];

                query = wxString::Format(wxT(
                    "INSERT INTO plsets(plset_id, plset_plid, plset_songid, plset_type, plset_option, plset_text, plset_number, plset_option2 ) "
                    "VALUES(NULL, %u, 0, %u, %u, '%s', %u, %u );"),
                                         PlayListId,
                                         FilterItem->m_Type,
                                         FilterItem->m_Option,
                                         escape_query_str(FilterItem->m_Text).c_str(),
                                         FilterItem->m_Number,
                                         FilterItem->m_Option2
                );

                ExecuteUpdate(query);
            }
        }

        return PlayListId;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListsCount()
    {
        int RetVal = 0;

        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT COUNT( playlist_id ) FROM playlists");

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            RetVal = dbRes.GetInt(0);
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetPlayLists(guListItems &playlists)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT playlist_id, playlist_name FROM playlists ORDER BY playlist_name");

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            playlists.Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));
        }

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetPlayLists(wxArrayString &playlistnames)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT playlist_name FROM playlists");

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            playlistnames.Add(dbRes.GetString(0));
        }

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetPlayLists(wxArrayInt &playlistids)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT playlist_id FROM playlists");

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            playlistids.Add(dbRes.GetInt(0));
        }

        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetPlayLists(guListItems *PlayLists, const int type, const wxArrayString *textfilters)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxString::Format(wxT("SELECT playlist_id, playlist_name FROM playlists "
            "WHERE playlist_type = %u "), type);

        if (textfilters && textfilters->Count())
        {
            int Count = textfilters->Count();
            for (int Index = 0; Index < Count; Index++)
            {
                wxString Filter = escape_query_str((*textfilters)[Index]);
                query += wxT("AND playlist_name LIKE '%") + Filter + wxT("%' ");
            }
        }

        query += wxT("ORDER BY playlist_name COLLATE NOCASE;"),

                dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            PlayLists->Add(new guListItem(dbRes.GetInt(0), dbRes.GetString(1)));
        }
        dbRes.Finalize();
        //  return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    const wxString DynPLStringOption(int option, const wxString &text)
    {
        wxString TextParam = text;
        escape_query_str(&TextParam);
        wxString FmtStr;
        switch (option)
        {
            case guDYNAMIC_FILTER_OPTION_STRING_CONTAINS: // contains
                FmtStr = wxT("LIKE '%%%s%%'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_NOT_CONTAINS: // not contains
                FmtStr = wxT("NOT LIKE '%%%s%%'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_IS: // IS
                FmtStr = wxT("= '%s'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_ISNOT: // IS NOT
                FmtStr = wxT("!= '%s'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_START_WITH: // START WITH
                FmtStr = wxT("LIKE '%s%%'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_ENDS_WITH: // ENDS WITH
                FmtStr = wxT("LIKE '%%%s'");
                break;
        }
        return wxString::Format(FmtStr, TextParam.c_str());
    }

    // -------------------------------------------------------------------------------- //
    const wxString DynPLLabelOption(int option, const wxString &text)
    {
        wxString TextParam = text;
        escape_query_str(&TextParam);
        wxString FmtStr;
        switch (option)
        {
            case guDYNAMIC_FILTER_OPTION_STRING_NOT_CONTAINS: // not contains
            case guDYNAMIC_FILTER_OPTION_STRING_CONTAINS: // contains
                FmtStr = wxT("LIKE '%%%s%%'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_IS: // IS
            case guDYNAMIC_FILTER_OPTION_STRING_ISNOT: // IS NOT
                FmtStr = wxT("= '%s'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_START_WITH: // START WITH
                FmtStr = wxT("LIKE '%s%%'");
                break;
            case guDYNAMIC_FILTER_OPTION_STRING_ENDS_WITH: // ENDS WITH
                FmtStr = wxT("LIKE '%%%s'");
                break;
        }
        return wxString::Format(FmtStr, TextParam.c_str());
    }

    // -------------------------------------------------------------------------------- //
    const wxString DynPLYearOption(const int option, const int year)
    {
        wxString FmtStr;
        switch (option)
        {
            case guDYNAMIC_FILTER_OPTION_YEAR_IS:
                FmtStr = wxT("= %u");
                break;
            case guDYNAMIC_FILTER_OPTION_YEAR_ISNOT:
                FmtStr = wxT("!= %u");
                break;
            case guDYNAMIC_FILTER_OPTION_YEAR_AFTER:
                FmtStr = wxT("> %u");
                break;
            case guDYNAMIC_FILTER_OPTION_YEAR_BEFORE:
                FmtStr = wxT("< %u");
                break;
        }
        return wxString::Format(FmtStr, year);
    }

    // -------------------------------------------------------------------------------- //
    const wxString DynPLNumericOption(const int option, const int value)
    {
        wxString FmtStr;
        switch (option)
        {
            case guDYNAMIC_FILTER_OPTION_NUMERIC_IS:
                FmtStr = wxT("= %u");
                break;
            case guDYNAMIC_FILTER_OPTION_NUMERIC_ISNOT:
                FmtStr = wxT("!= %u");
                break;
            case guDYNAMIC_FILTER_OPTION_NUMERIC_AT_LEAST:
                FmtStr = wxT(">= %u");
                break;
            case guDYNAMIC_FILTER_OPTION_NUMERIC_AT_MOST:
                FmtStr = wxT("<= %u");
                break;
        }
        return wxString::Format(FmtStr, value);
    }

    // -------------------------------------------------------------------------------- //
    unsigned long DynPLDateOption2[] = {
        60, // Minutes
        3600, // Hours
        86400, // Days
        604800, // Weeks
        2592000 // Months
    };

    // -------------------------------------------------------------------------------- //
    const wxString DynPLDateOption(const int option, const int value, const int option2)
    {
        unsigned long Time;
        wxString FmtStr;

        switch (option)
        {
            case guDYNAMIC_FILTER_OPTION_DATE_IN_THE_LAST: // IN_THE_LAST
            {
                FmtStr = wxT(">= %lu");
                break;
            }

            case guDYNAMIC_FILTER_OPTION_DATE_BEFORE_THE_LAST: // BEFORE_THE_LAST
            {
                FmtStr = wxT("< %lu");
                break;
            }
        }
        Time = wxDateTime::GetTimeNow() - (value * DynPLDateOption2[option2]);
        return wxString::Format(FmtStr, Time);
    }

    // -------------------------------------------------------------------------------- //
    const wxString DynPlayListToSQLQuery(guDynPlayList *playlist)
    {
        if (!playlist->m_Filters.Count())
            return wxEmptyString;

        wxString query = wxEmptyString;
        wxString dbNames = wxEmptyString;
        wxString dbSets = wxEmptyString;
        int count = playlist->m_Filters.Count();
        for (int index = 0; index < count; index++)
        {
            if (index)
                query += playlist->m_AnyOption ? wxT(" OR  ") : wxT(" AND ");

            switch (playlist->m_Filters[index].m_Type)
            {
                case guDYNAMIC_FILTER_TYPE_TITLE: // TITLE
                    query += wxT("song_name ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                   playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_ARTIST: // ARTIST
                    query += wxT("song_artist ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                     playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_ALBUMARTIST: // ALBUMARTIST
                    query += wxT("song_albumartist ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                          playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_ALBUM: // ALBUM
                    query += wxT("song_album ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                    playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_GENRE: // GENRE
                    query += wxT("song_genre ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                    playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_LABEL: // LABEL
                    if (playlist->m_Filters[index].m_Option == guDYNAMIC_FILTER_OPTION_LABELS_NOTSET)
                    {
                        query += wxT(
                            "( song_id NOT IN ( SELECT DISTINCT settag_songid FROM settags WHERE settag_songid > 0 ) ) ");
                    } else
                    {
                        wxString TagNameFilter = wxT("SELECT tag_id FROM tags WHERE tag_name ") +
                                                 DynPLLabelOption(playlist->m_Filters[index].m_Option,
                                                                  playlist->m_Filters[index].m_Text);
                        wxString Option;
                        wxString Condition;
                        switch (playlist->m_Filters[index].m_Option)
                        {
                            case guDYNAMIC_FILTER_OPTION_STRING_CONTAINS: // contains
                            case guDYNAMIC_FILTER_OPTION_STRING_IS: // IS
                            case guDYNAMIC_FILTER_OPTION_STRING_START_WITH: // START WITH
                            case guDYNAMIC_FILTER_OPTION_STRING_ENDS_WITH: // ENDS WITH
                                Option = wxT("IN ");
                                Condition = wxT("OR ");
                                break;

                            case guDYNAMIC_FILTER_OPTION_STRING_NOT_CONTAINS: // not contains
                            case guDYNAMIC_FILTER_OPTION_STRING_ISNOT: // IS NOT
                                Option = wxT("NOT IN ");
                                Condition = wxT("AND ");
                                break;
                        }
                        query += wxT("((song_artistid ") + Option + wxT(
                            "(SELECT settag_artistid FROM settags WHERE settag_tagid IN (");
                        query += TagNameFilter + wxT(") and settag_artistid > 0 )) ") + Condition;
                        query += wxT("(song_albumid ") + Option + wxT(
                            "(SELECT settag_albumid FROM settags WHERE settag_tagid IN (");
                        query += TagNameFilter + wxT(") and settag_albumid > 0 )) ") + Condition;
                        query += wxT("(song_id ") + Option + wxT(
                            "(SELECT settag_songid FROM settags WHERE settag_tagid IN (");
                        query += TagNameFilter + wxT(") and settag_songid > 0 )))");
                    }
                    break;

                case guDYNAMIC_FILTER_TYPE_COMPOSER: // COMPOSER
                    query += wxT("song_composer ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                       playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_COMMENT: // COMMENT
                    query += wxT("song_comment ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                      playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_PATH: // PATH
                    query += wxT("song_path ") + DynPLStringOption(playlist->m_Filters[index].m_Option,
                                                                   playlist->m_Filters[index].m_Text);
                    break;

                case guDYNAMIC_FILTER_TYPE_YEAR: // YEAR
                {
                    query += wxT("( song_year ") +
                            DynPLYearOption(playlist->m_Filters[index].m_Option,
                                            playlist->m_Filters[index].m_Number) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_RATING: // RATING
                {
                    query += wxT("( song_rating ") +
                            DynPLNumericOption(playlist->m_Filters[index].m_Option,
                                               playlist->m_Filters[index].m_Number) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_LENGTH: // LENGTH
                {
                    query += wxT("( song_length ") +
                            DynPLNumericOption(playlist->m_Filters[index].m_Option,
                                               playlist->m_Filters[index].m_Number * 1000) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_PLAYCOUNT: // PLAYCOUNT
                {
                    query += wxT("( song_playcount ") +
                            DynPLNumericOption(playlist->m_Filters[index].m_Option,
                                               playlist->m_Filters[index].m_Number) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_LASTPLAY:
                {
                    query += wxT("( IFNULL( song_lastplay, 0 ) ") +
                            DynPLDateOption(playlist->m_Filters[index].m_Option,
                                            playlist->m_Filters[index].m_Number,
                                            playlist->m_Filters[index].m_Option2) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_ADDEDDATE:
                {
                    query += wxT("( song_addedtime ") +
                            DynPLDateOption(playlist->m_Filters[index].m_Option,
                                            playlist->m_Filters[index].m_Number,
                                            playlist->m_Filters[index].m_Option2) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_TRACKNUMBER: // TRACK NUMBER
                {
                    query += wxT("( song_number ") +
                            DynPLNumericOption(playlist->m_Filters[index].m_Option,
                                               playlist->m_Filters[index].m_Number) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_BITRATE: // BITRATE
                {
                    query += wxT("( song_bitrate ") +
                            DynPLNumericOption(playlist->m_Filters[index].m_Option,
                                               playlist->m_Filters[index].m_Number) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_SIZE: // SIZE
                {
                    query += wxT("( song_filesize ") +
                            DynPLNumericOption(playlist->m_Filters[index].m_Option,
                                               playlist->m_Filters[index].m_Number) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_DISK: // DISK
                {
                    query += wxT("( song_disk ") +
                            DynPLStringOption(playlist->m_Filters[index].m_Option,
                                              playlist->m_Filters[index].m_Text) + wxT(")");
                    break;
                }

                case guDYNAMIC_FILTER_TYPE_HASARTWORK: // HAS ARTWORK
                {
                    query += wxT("( song_coverid ");
                    query += (playlist->m_Filters[index].m_Option ? wxT("> 0 )") : wxT("< 1 )"));
                    break;
                }
            }
        }
        // SORTING Options
        //guLogMessage( wxT( "Sort: %u %u %u" ), playlist->m_Sorted, playlist->m_SortType, playlist->m_SortDesc );
        wxString sort;
        if (playlist->m_Sorted)
        {
            sort = wxT(" ORDER BY ");
            switch (playlist->m_SortType)
            {
                case guDYNAMIC_FILTER_ORDER_TITLE: sort += wxT("song_name");
                    break;
                case guDYNAMIC_FILTER_ORDER_ARTIST: sort += wxT("song_artist");
                    break;
                case guDYNAMIC_FILTER_ORDER_ALBUMARTIST: sort += wxT("song_albumartist");
                    break;
                case guDYNAMIC_FILTER_ORDER_ALBUM: sort += wxT("song_album");
                    break;
                case guDYNAMIC_FILTER_ORDER_GENRE: sort += wxT("song_genre");
                    break;
                case guDYNAMIC_FILTER_ORDER_COMPOSER: sort += wxT("song_composer");
                    break;
                case guDYNAMIC_FILTER_ORDER_YEAR: sort += wxT("song_year");
                    break;
                case guDYNAMIC_FILTER_ORDER_RATING: sort += wxT("song_rating");
                    break;
                case guDYNAMIC_FILTER_ORDER_LENGTH: sort += wxT("song_length");
                    break;
                case guDYNAMIC_FILTER_ORDER_PLAYCOUNT: sort += wxT("song_playcount");
                    break;
                case guDYNAMIC_FILTER_ORDER_LASTPLAY: sort += wxT("song_lastplay");
                    break;
                case guDYNAMIC_FILTER_ORDER_ADDEDDATE: sort += wxT("song_addedtime");
                    break;
                case guDYNAMIC_FILTER_ORDER_RANDOM: sort += wxT("RANDOM()");
                    break;
            }

            if (playlist->m_SortDesc)
                sort += wxT(" DESC");

            switch (playlist->m_SortType)
            {
                case guDYNAMIC_FILTER_ORDER_ARTIST: sort += wxT(", song_album, song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_ALBUMARTIST: sort += wxT(", song_album, song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_ALBUM: sort += wxT(", song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_GENRE: sort += wxT(", song_artist, song_album, song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_COMPOSER: sort += wxT(", song_album, song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_YEAR: sort += wxT(", song_album, song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_RATING: sort += wxT(", song_album, song_disk, song_number");
                    break;
                case guDYNAMIC_FILTER_ORDER_ADDEDDATE: sort += wxT(", song_album, song_disk, song_number");
                    break;
            }
        }

        //guLogMessage( wxT( "..., %s%s%s" ), dbNames.c_str(), query.c_str(), sort.c_str() );
        if (!dbSets.IsEmpty())
            dbSets = wxT("( ") + dbSets + wxT(") AND ");

        return dbNames + wxT(" WHERE ") + dbSets + wxT("(") + query + wxT(")") + sort;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListSongIds(const int plid, wxArrayInt *tracks)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query += wxString::Format(wxT("SELECT plset_songid FROM plsets WHERE plset_plid = %u"), plid);

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            tracks->Add(dbRes.GetInt(0));;
        }
        dbRes.Finalize();

        return tracks->Count();
    }

    // -------------------------------------------------------------------------------- //
    wxString guDbLibrary::GetPlayListQuery(const int plid)
    {
        wxString RetVal = wxEmptyString;
        if (plid)
        {
            int PlType = GetPlayListType(plid);

            if (PlType == guPLAYLIST_TYPE_STATIC)
                RetVal = wxString::Format(wxT("SELECT plset_songid FROM plsets WHERE plset_plid = %u"), plid);
            else if (PlType == guPLAYLIST_TYPE_DYNAMIC)
            {
                guDynPlayList PlayList;
                GetDynamicPlayList(plid, &PlayList);

                RetVal = wxT("SELECT song_id FROM songs ") + DynPlayListToSQLQuery(&PlayList);
            }
        }
        //guLogMessage( wxT( "::GetPlaylistQuery %s"), RetVal.c_str() );
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListSongs(const int plid, guTrackArray *tracks)
    {
        int PlType = GetPlayListType(plid);
        if (PlType != wxNOT_FOUND)
            return GetPlayListSongs(plid, PlType, tracks, nullptr, nullptr);

        return 0;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListSongs(const int plid, const int pltype, guTrackArray *tracks,
                                      wxArrayInt *setids, wxLongLong *len, wxLongLong *size,
                                      const wxArrayInt *order, const wxArrayInt *orderdesc)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Track;
        wxLongLong TrackLength = 0;
        wxLongLong TrackSize = 0;

        if (plid)
        {
            if (pltype == guPLAYLIST_TYPE_STATIC)
            {
                query = wxT("SELECT song_id, song_name, song_genreid, song_genre, song_artistid, song_artist, "
                    "song_albumartistid, song_albumartist, song_composerid, song_composer, song_albumid, song_album, "
                    "song_pathid, song_path, song_filename, song_format, song_disk, song_number, song_year, song_comment, "
                    "song_coverid, song_offset, song_length, song_bitrate, song_rating, song_playcount, "
                    "song_addedtime, song_lastplay, song_filesize, plset_id "
                    "FROM songs ");
                query += wxString::Format(wxT(", plsets WHERE plset_songid = song_id AND plset_plid = %u"), plid);

                query += GetSongsSortSQL(order, orderdesc);

                dbRes = ExecuteQuery(query);

                while (dbRes.NextRow())
                {
                    Track = new guTrack();
                    FillTrackFromDb(Track, &dbRes);
                    tracks->Add(Track);
                    TrackLength += Track->m_Length;
                    TrackSize += Track->m_FileSize;
                    if (setids)
                        setids->Add(dbRes.GetInt(29));
                }
                dbRes.Finalize();
            } else //guPLAYLIST_TYPE_DYNAMIC
            {
                guDynPlayList PlayList;
                GetDynamicPlayList(plid, &PlayList);
                wxLongLong Limit;
                wxLongLong Count = 0;
                if (PlayList.m_Limited)
                {
                    switch (PlayList.m_LimitType)
                    {
                        case guDYNAMIC_FILTER_LIMIT_TRACKS: // TRACKS
                            Limit = PlayList.m_LimitValue;
                            break;

                        case guDYNAMIC_FILTER_LIMIT_MINUTES: // Minutes -> to seconds
                            Limit = PlayList.m_LimitValue * 60000;
                            break;

                        case guDYNAMIC_FILTER_LIMIT_MEGABYTES: // MB -> To bytes
                            Limit = PlayList.m_LimitValue;
                            Limit *= 1000000;
                            break;

                        case guDYNAMIC_FILTER_LIMIT_GIGABYTES: // GB -> to bytes
                            Limit = PlayList.m_LimitValue;
                            Limit *= 1000000000;
                            break;
                    }
                }

                query = wxT("SELECT song_id, song_name, song_genreid, song_genre, song_artistid, song_artist, "
                    "song_albumartistid, song_albumartist, song_composerid, song_composer, song_albumid, song_album, "
                    "song_pathid, song_path, song_filename, song_format, song_disk, song_number, song_year, song_comment, "
                    "song_coverid, song_offset, song_length, song_bitrate, song_rating, song_playcount, "
                    "song_addedtime, song_lastplay, song_filesize "
                    "FROM songs ");
                query += DynPlayListToSQLQuery(&PlayList);

                ////if (!PlayList.m_Sorted && (*order != wxNOT_FOUND))
                if (!PlayList.m_Sorted && !order->IsEmpty())
                    query += GetSongsSortSQL(order, orderdesc);

                //guLogMessage( wxT( "GetPlayListSongs: <<%s>>" ), query.c_str() );

                dbRes = ExecuteQuery(query);

                while (dbRes.NextRow())
                {
                    guTrack *Track = new guTrack();
                    FillTrackFromDb(Track, &dbRes);
                    if (PlayList.m_Limited)
                    {
                        //guLogMessage( wxT( "Limit: %lli / %lli" ), Count.GetValue(), Limit.GetValue() );

                        if (PlayList.m_LimitType == guDYNAMIC_FILTER_LIMIT_TRACKS)
                            Count++;
                        else if (PlayList.m_LimitType == guDYNAMIC_FILTER_LIMIT_MINUTES)
                            Count += Track->m_Length;
                        else
                            Count += Track->m_FileSize;

                        if (Count > Limit)
                            break;
                    }
                    tracks->Add(Track);
                    TrackLength += Track->m_Length;
                    TrackSize += Track->m_FileSize;
                }
                dbRes.Finalize();
            }
        }
        if (len)
            *len = TrackLength;
        if (size)
            *size = TrackSize;
        return tracks->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListSongs(const wxArrayInt &ids, const wxArrayInt &types, guTrackArray *tracks,
                                      wxArrayInt *setids, wxLongLong *len, wxLongLong *size,
                                      const wxArrayInt *order, const wxArrayInt *orderdesc)
    {
        int Count = wxMin(ids.Count(), types.Count());
        if (Count == 1)
            return GetPlayListSongs(ids[0], types[0], tracks, setids, len, size, order, orderdesc);

        //
        // Only get multiple ids for static playlists
        //
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Track;
        wxLongLong TrackLength = 0;
        wxLongLong TrackSize = 0;

        query = wxT("SELECT song_id, song_name, song_genreid, song_genre, song_artistid, song_artist, "
            "song_albumartistid, song_albumartist, song_composerid, song_composer, song_albumid, song_album, "
            "song_pathid, song_path, song_filename, song_format, song_disk, song_number, song_year, song_comment, "
            "song_coverid, song_offset, song_length, song_bitrate, song_rating, song_playcount, "
            "song_addedtime, song_lastplay, song_filesize, plset_id "
            "FROM songs ");
        query += wxT(", plsets WHERE plset_songid = song_id AND plset_plid IN (");
        for (int Index = 0; Index < Count; Index++)
        {
            query += wxString::Format(wxT("%u,"), ids[Index]);
        }
        query.RemoveLast(1);
        query += wxT(")") + GetSongsSortSQL(order, orderdesc);

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            Track = new guTrack();
            FillTrackFromDb(Track, &dbRes);
            tracks->Add(Track);
            TrackLength += Track->m_Length;
            TrackSize += Track->m_FileSize;
            if (setids)
                setids->Add(dbRes.GetInt(29));
        }
        dbRes.Finalize();

        if (len)
            *len = TrackLength;
        if (size)
            *size = TrackSize;

        return tracks->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListSetIds(const int plid, wxArrayInt *setids)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT plset_id FROM plsets WHERE plset_plid = ");
        query += wxString::Format(wxT("%u"), plid);

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            setids->Add(dbRes.GetInt(0));
        }
        dbRes.Finalize();

        return setids->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListSetIds(const wxArrayInt &plids, wxArrayInt *setids)
    {
        int Count = plids.Count();
        for (int Index = 0; Index < Count; Index++)
            GetPlayListSetIds(plids[Index], setids);

        return setids->Count();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetDynamicPlayList(const int plid, guDynPlayList *playlist)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxString::Format(wxT("SELECT playlist_name, playlist_limited, playlist_limitvalue, playlist_limittype, "
            "playlist_sorted, playlist_sorttype, playlist_sortdesc, playlist_anyoption "
            "FROM playlists WHERE playlist_id = %u LIMIT 1;"), plid);
        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
        {
            playlist->m_Id = plid;
            playlist->m_Name = dbRes.GetString(0);
            playlist->m_Limited = dbRes.GetBool(1);
            playlist->m_LimitValue = dbRes.GetInt(2);
            playlist->m_LimitType = dbRes.GetInt(3);
            playlist->m_Sorted = dbRes.GetBool(4);
            playlist->m_SortType = dbRes.GetInt(5);
            playlist->m_SortDesc = dbRes.GetBool(6);
            playlist->m_AnyOption = dbRes.GetBool(7);
        }
        dbRes.Finalize();

        if (playlist->m_Id)
        {
            query = wxString::Format(wxT("SELECT plset_type, plset_option, plset_text, plset_number, plset_option2 "
                "FROM plsets WHERE plset_plid = %u;"), plid);
            dbRes = ExecuteQuery(query);
            while (dbRes.NextRow())
            {
                guFilterItem *FilterItem = new guFilterItem();
                FilterItem->m_Type = dbRes.GetInt(0);
                FilterItem->m_Option = dbRes.GetInt(1);
                FilterItem->m_Text = dbRes.GetString(2);
                FilterItem->m_Number = dbRes.GetInt(3);
                FilterItem->m_Option2 = dbRes.GetInt(4);
                FilterItem->SetFilterLabel();
                playlist->m_Filters.Add(FilterItem);
            }
            dbRes.Finalize();
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateDynamicPlayList(const int plid, const guDynPlayList *playlist)
    {
        wxString query;

        query = wxString::Format(wxT("UPDATE playlists SET playlist_name = '%s', "
            "playlist_limited = %u, playlist_limitvalue = %u, playlist_limittype = %u, "
            "playlist_sorted = %u, playlist_sorttype = %u, playlist_sortdesc = %u, "
            "playlist_anyoption = %u WHERE playlist_id = %u;"),
                                 escape_query_str(playlist->m_Name).c_str(),
                                 playlist->m_Limited, playlist->m_LimitValue, playlist->m_LimitType,
                                 playlist->m_Sorted, playlist->m_SortType, playlist->m_SortDesc,
                                 playlist->m_AnyOption,
                                 plid);

        ExecuteUpdate(query);

        query = wxString::Format(wxT("DELETE FROM plsets WHERE plset_plid = %u;"), plid);
        ExecuteUpdate(query);

        int count = playlist->m_Filters.Count();
        for (int index = 0; index < count; index++)
        {
            guFilterItem *FilterItem = &playlist->m_Filters[index];

            query = wxString::Format(wxT("INSERT INTO plsets(plset_id, plset_plid, plset_songid, "
                "plset_type, plset_option, plset_text, plset_number, plset_option2 ) "
                "VALUES(NULL, %u, 0, %u, %u, '%s', %u, %u );"),
                                     plid,
                                     FilterItem->m_Type,
                                     FilterItem->m_Option,
                                     escape_query_str(FilterItem->m_Text).c_str(),
                                     FilterItem->m_Number,
                                     FilterItem->m_Option2
            );

            ExecuteUpdate(query);
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::DeletePlayList(const int plid)
    {
        wxString query;

        if (plid)
        {
            query = wxString::Format(wxT("DELETE FROM plsets WHERE plset_plid = %u;"), plid);
            ExecuteUpdate(query);

            query = wxString::Format(wxT("DELETE FROM playlists WHERE playlist_id = %u;"), plid);
            ExecuteUpdate(query);
        }
    }

    // -------------------------------------------------------------------------------- //
    wxString guDbLibrary::GetPlayListName(const int plid)
    {
        wxString RetVal;
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxString::Format(wxT("SELECT playlist_name FROM playlists WHERE playlist_id = %u LIMIT 1;"), plid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetString(0);

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    wxString guDbLibrary::GetPlayListPath(const int plid)
    {
        wxString RetVal;
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxString::Format(wxT("SELECT playlist_path FROM playlists WHERE playlist_id = %u LIMIT 1;"), plid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetString(0);

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetPlayListPath(const int plid, const wxString &path)
    {
        wxString query;

        if (plid)
        {
            query = wxString::Format(wxT("UPDATE playlists SET playlist_path = '%s' WHERE playlist_id = %u;"),
                                     escape_query_str(path).c_str(), plid);
            ExecuteUpdate(query);
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetPlayListName(const int plid, const wxString &plname)
    {
        wxString query;

        if (plid)
        {
            query = wxString::Format(wxT("UPDATE playlists SET playlist_name = '%s' WHERE playlist_id = %u;"),
                                     escape_query_str(plname).c_str(), plid);
            ExecuteUpdate(query);
        }
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetLabelsSongs(const wxArrayInt &Labels, guTrackArray *Songs)
    {
        wxString subquery;
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;

        if (Labels.Count())
        {
            subquery = ArrayIntToStrList(Labels);

            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("( (song_artistid IN ( SELECT settag_artistid FROM settags WHERE "
                "settag_tagid IN ") + subquery + wxT(" and settag_artistid > 0 ) ) OR");
            query += wxT(" (song_albumid IN ( SELECT settag_albumid FROM settags WHERE "
                "settag_tagid IN ") + subquery + wxT(" and settag_albumid > 0 ) ) OR");
            query += wxT(" (song_id IN ( SELECT settag_songid FROM settags WHERE "
                "settag_tagid IN ") + subquery + wxT(" and settag_songid > 0 ) ) ) ");
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetGenresSongs(const wxArrayInt &Genres, guTrackArray *Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;

        if (Genres.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            bool has_where = query.Find(wxT("WHERE")) != wxNOT_FOUND;
            query += has_where ? wxT("AND ") : wxT("WHERE ");
            query += wxT("song_genreid IN ") + ArrayIntToStrList(Genres);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }

        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetArtistsSongs(const wxArrayInt &Artists, guTrackArray *Songs, guTrackMode trackmode)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;
        if (Artists.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_artistid IN ") + ArrayIntToStrList(Artists);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Song->m_TrackMode = trackmode;
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetArtistsSongs(const wxArrayInt &Artists, guTrackArray *Songs,
                                     const int trackcount, const int filterallow, const int filterdeny)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;

        if (Artists.Count())
        {
            wxString AllowPlQuery = GetPlayListQuery(filterallow);
            wxString DenyPlQuery = GetPlayListQuery(filterdeny);

            wxString Filters = wxEmptyString;
            if (!AllowPlQuery.IsEmpty())
                Filters += wxT("WHERE song_id IN ( ") + AllowPlQuery + wxT(" ) ");

            if (!DenyPlQuery.IsEmpty())
            {
                Filters += Filters.IsEmpty() ? wxT("WHERE ") : wxT("AND ");
                Filters += wxT("song_id NOT IN ( ") + DenyPlQuery + wxT(" ) ");
            }

            query = GU_TRACKS_QUERYSTR;

            query += Filters;

            if (!Filters.IsEmpty())
                query += wxT(" AND");
            else
                query += wxT(" WHERE");

            query += wxT(" song_artistid IN ") + ArrayIntToStrList(Artists);
            query += wxString::Format(wxT(" ORDER BY RANDOM() LIMIT %u"), trackcount);

            //guLogMessage( wxT( "GetArtistsSongs:\n%s" ), query.c_str() );
            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Song->m_TrackMode = guTRACK_MODE_SMART;
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetArtistsAlbums(const wxArrayInt &Artists, wxArrayInt *Albums)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        if (Artists.Count())
        {
            query = wxT("SELECT DISTINCT song_albumid FROM songs WHERE song_artistid IN ");
            query += ArrayIntToStrList(Artists);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
                Albums->Add(dbRes.GetInt(0));

            dbRes.Finalize();
        }
        return Albums->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumArtistsAlbums(const wxArrayInt &albumartists, wxArrayInt *Albums)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        if (albumartists.Count())
        {
            query = wxT("SELECT DISTINCT song_albumid FROM songs WHERE song_albumartistid IN ");
            query += ArrayIntToStrList(albumartists);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
                Albums->Add(dbRes.GetInt(0));

            dbRes.Finalize();
        }
        return Albums->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumsSongs(const wxArrayInt &Albums, guTrackArray *Songs, bool ordertoedit)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;
        if (Albums.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_albumid IN ") + ArrayIntToStrList(Albums);
            if (ordertoedit)
                query += wxT(" ORDER BY song_disk, song_number, song_filename");
            else
                query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            //guLogMessage( wxT( "GetAlbumsSongs: %s" ), query.c_str() );
            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetYearsSongs(const wxArrayInt &Years, guTrackArray *Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;
        if (Years.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_year IN ") + ArrayIntToStrList(Years);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetRatingsSongs(const wxArrayInt &Ratings, guTrackArray *Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;
        if (Ratings.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_rating IN ") + ArrayIntToStrList(Ratings);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayCountsSongs(const wxArrayInt &PlayCounts, guTrackArray *Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;
        if (PlayCounts.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_playcount IN ") + ArrayIntToStrList(PlayCounts);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumArtistsSongs(const wxArrayInt &albumartists, guTrackArray *tracks)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Track;
        if (albumartists.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_albumartistid IN ") + ArrayIntToStrList(albumartists);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Track = new guTrack();
                FillTrackFromDb(Track, &dbRes);
                tracks->Add(Track);
            }
            dbRes.Finalize();
        }
        return tracks->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetComposersSongs(const wxArrayInt &Composers, guTrackArray *Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;
        if (Composers.Count())
        {
            query = GU_TRACKS_QUERYSTR;
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += wxT("song_composerid IN ") + ArrayIntToStrList(Composers);
            query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

            dbRes = ExecuteQuery(query);

            while (dbRes.NextRow())
            {
                Song = new guTrack();
                FillTrackFromDb(Song, &dbRes);
                Songs->Add(Song);
            }
            dbRes.Finalize();
        }
        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetPlayListType(const int plid)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;

        query = wxString::Format(wxT("SELECT playlist_type FROM playlists WHERE playlist_id = %u LIMIT 1;"), plid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            RetVal = dbRes.GetInt(0);

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetRandomTracks(guTrackArray *tracks, const int count, const int rndmode,
                                     const int allowplaylist, const int denyplaylist)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Track;
        wxString AllowPlQuery = wxEmptyString;
        wxString DenyPlQuery = wxEmptyString;

        //guLogMessage( wxT( "GetRandomTracks... %i %i %i" ), rndmode, allowplaylist, denyplaylist );

        AllowPlQuery = GetPlayListQuery(allowplaylist);
        DenyPlQuery = GetPlayListQuery(denyplaylist);

        wxString Filters = wxEmptyString;
        if (!AllowPlQuery.IsEmpty())
            Filters += wxT("WHERE song_id IN ( ") + AllowPlQuery + wxT(" ) ");

        if (!DenyPlQuery.IsEmpty())
        {
            Filters += Filters.IsEmpty() ? wxT("WHERE ") : wxT("AND ");
            Filters += wxT("song_id NOT IN ( ") + DenyPlQuery + wxT(" ) ");
        }

        if (rndmode == guRANDOM_MODE_TRACK)
        {
            query = GU_TRACKS_QUERYSTR + Filters;
            query += wxString::Format(wxT(" ORDER BY RANDOM() LIMIT %u;"), count);
        } else //if( rndmode == guRANDOM_MODE_ALBUM )
        {
            int AlbumId = 0;
            query = wxT("SELECT song_albumid FROM songs ") + Filters;
            query += wxT(" ORDER BY RANDOM() LIMIT 1;");
            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                AlbumId = dbRes.GetInt(0);

            dbRes.Finalize();
            query = GU_TRACKS_QUERYSTR + wxString::Format(wxT("WHERE song_albumid = %i"), AlbumId);
            query += wxT(" ORDER BY song_disk, song_number, song_filename");
        }

        //guLogMessage( wxT( "GetRandomTracks:\n%s" ), query.c_str() );

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            Track = new guTrack();
            FillTrackFromDb(Track, &dbRes);
            Track->m_TrackMode = guTRACK_MODE_RANDOM;
            tracks->Add(Track);
        }
        dbRes.Finalize();

        return tracks->Count();
    }

    // -------------------------------------------------------------------------------- //
    const wxString guDbLibrary::GetArtistName(const int ArtistId)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxString RetVal = wxEmptyString;

        query = wxString::Format(wxT("SELECT song_artist FROM songs WHERE song_artistid = %u LIMIT 1;"), ArtistId);

        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
            RetVal = dbRes.GetString(0);

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetArtistId(wxString &artistname, bool create)
    {
        if (m_LastArtist == artistname)
            return m_LastArtistId;

        // TODO Add a Lock
        wxString query;
        wxSQLite3ResultSet dbRes;

        m_LastArtist = artistname;

        query = wxString::Format(wxT("SELECT song_artistid FROM songs "
            "WHERE song_artist = '%s' LIMIT 1;"),
                                 escape_query_str(artistname).c_str());

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            return m_LastArtistId = dbRes.GetInt(0);
        else if (create)
        {
            query = wxT("SELECT MAX(song_artistid) FROM songs");

            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                return m_LastArtistId = dbRes.GetInt(0) + 1;
            else
                return m_LastArtistId = 1;
        }

        return wxNOT_FOUND;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumArtistId(wxString &albumartist, bool create)
    {
        if (m_LastAlbumArtist == albumartist)
            return m_LastAlbumArtistId;

        // TODO Add a Lock
        wxString query;
        wxSQLite3ResultSet dbRes;

        m_LastAlbumArtist = albumartist;

        query = wxString::Format(wxT("SELECT song_albumartistid FROM songs "
            "WHERE song_albumartist = '%s' LIMIT 1;"),
                                 escape_query_str(albumartist).c_str());

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            return m_LastAlbumArtistId = dbRes.GetInt(0);
        else if (create)
        {
            query = wxT("SELECT MAX(song_albumartistid) FROM songs;");

            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                return m_LastAlbumArtistId = dbRes.GetInt(0) + 1;
            else
                return m_LastAlbumArtistId = 1;
        }

        return wxNOT_FOUND;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::FindArtist(const wxString &Artist)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxString::Format(wxT("SELECT song_artistid FROM songs WHERE song_artist = '%s' LIMIT 1;"),
                                 escape_query_str(Artist).c_str());

        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
            return dbRes.GetInt(0);

        return wxNOT_FOUND;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::FindAlbum(const wxString &Artist, const wxString &Album)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        int ArtistId = FindArtist(Artist);
        if (ArtistId != wxNOT_FOUND)
        {
            query = wxString::Format(wxT("SELECT song_albumid FROM songs WHERE "
                "song_artistid = %d AND song_album = '%s' LIMIT 1;"),
                                     ArtistId,
                                     escape_query_str(Album).c_str());

            dbRes = ExecuteQuery(query);
            if (dbRes.NextRow())
                return dbRes.GetInt(0);
        }
        return wxNOT_FOUND;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::FindTrack(const wxString &Artist, const wxString &Name)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        int ArtistId = FindArtist(Artist);
        if (ArtistId != wxNOT_FOUND)
        {
            query = wxString::Format(wxT("SELECT song_id FROM songs WHERE ")\
                                     wxT("song_artistid = %d AND song_name = '%s' LIMIT 1;"),
                                     ArtistId,
                                     escape_query_str(Name).c_str());

            dbRes = ExecuteQuery(query);

            if (dbRes.NextRow())
                return dbRes.GetInt(0);
        }
        return wxNOT_FOUND;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetTrackIndex(const int trackid)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int Index = 0;

        query = wxT("SELECT song_id FROM songs ");
        query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc, true);

        dbRes = (const wxSQLite3ResultSet) ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            if (dbRes.GetInt(0) == trackid)
                return Index;
            Index++;
        }
        return wxNOT_FOUND;
    }

    // -------------------------------------------------------------------------------- //
    guTrack *guDbLibrary::FindSong(const wxString &artist, const wxString &trackname,
                                   const int filterallow, const int filterdeny)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *RetVal = nullptr;
        wxString ArtistName = artist;
        wxString TrackName = trackname;

        wxString Filters = wxEmptyString;

        wxString AllowPlQuery = GetPlayListQuery(filterallow);
        if (!AllowPlQuery.IsEmpty())
            Filters += wxT("WHERE song_id IN ( ") + AllowPlQuery + wxT(" ) ");

        wxString DenyPlQuery = GetPlayListQuery(filterdeny);
        if (!DenyPlQuery.IsEmpty())
        {
            Filters += Filters.IsEmpty() ? wxT("WHERE ") : wxT("AND ");
            Filters += wxT("song_id NOT IN ( ") + DenyPlQuery + wxT(" ) ");
        }

        escape_query_str(&ArtistName);
        escape_query_str(&TrackName);

        query = GU_TRACKS_QUERYSTR;

        query += Filters;
        if (!Filters.IsEmpty())
            query += wxT(" AND");
        else
            query += wxT(" WHERE");
        query += wxString::Format(
            wxT(" song_artist = '%s' AND song_name = '%s' LIMIT 1;"), ArtistName.c_str(), TrackName.c_str());

        //guLogMessage( wxT( "FindSong:\n%s" ), query.c_str() );

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            RetVal = new guTrack();
            FillTrackFromDb(RetVal, &dbRes);
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::FindTrackFile(const wxString &filename, guTrack *track)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;
        wxString Path = GetPathAddTrailSep(wxPathOnly(filename));

        escape_query_str(&Path);

        query = GU_TRACKS_QUERYSTR +
                wxString::Format(wxT(" WHERE song_path = '%s' AND song_filename = '%s' LIMIT 1;"),
                                 Path.c_str(),
                                 escape_query_str(filename.AfterLast('/')).c_str());
        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
        {
            RetVal = dbRes.GetInt(0);
            if (track)
                FillTrackFromDb(track, &dbRes);
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- // m_Path, Track.m_FileName, Track.m_SongName
    int guDbLibrary::FindTrackPath(wxString path, wxString fileName, wxString songName, guTrack *track)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;

        AddPathTrailSep(path);
        escape_query_str((&path));
        escape_query_str(&fileName);
        escape_query_str(&songName);

        query = GU_TRACKS_QUERYSTR +
                wxString::Format(wxT(" WHERE song_path = '%s' AND song_filename = '%s' AND song_name = '%s' LIMIT 1;"),
                                 path.c_str(), fileName.c_str(), songName.c_str());
        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
        {
            RetVal = dbRes.GetInt(0);
            if (track)
                FillTrackFromDb(track, &dbRes);
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::FindTrackId(const int trackid, guTrack *track)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = 0;

        query = GU_TRACKS_QUERYSTR + wxString::Format(wxT(" WHERE song_id = %u LIMIT 1;"), trackid);

        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
        {
            RetVal = dbRes.GetInt(0);
            if (track)
                FillTrackFromDb(track, &dbRes);
        }

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::GetSong(const int songid, guTrack *song)
    {
        bool RetVal = false;
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = GU_TRACKS_QUERYSTR;
        query += wxString::Format(wxT("WHERE song_id = %i LIMIT 1;"), songid);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            FillTrackFromDb(song, &dbRes);
            RetVal = true;
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongs(const wxArrayInt &SongIds, guTrackArray *Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;

        query = GU_TRACKS_QUERYSTR;
        //query += ( query.Find( wxT( "WHERE" ) ) == wxNOT_FOUND ) ? wxT( "WHERE " ) : wxT( "AND " );
        query += wxT("WHERE song_id IN ") + ArrayIntToStrList(SongIds);
        query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);

        //guLogMessage( wxT( "%s" ), query.c_str() );

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            Song = new guTrack();
            FillTrackFromDb(Song, &dbRes);
            Songs->Add(Song);
        }
        dbRes.Finalize();

        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    wxString inline GetTreeViewFilterEntrySql(const guTreeViewFilterEntry &filterentry)
    {
        wxString RetVal = wxT("(");
        int Count = filterentry.Count();
        for (int Index = 0; Index < Count; Index++)
        {
            switch (filterentry[Index].m_Type)
            {
                case guLIBRARY_ELEMENT_GENRES:
                    RetVal += wxT("song_genreid=");
                    break;

                case guLIBRARY_ELEMENT_ARTISTS:
                    RetVal += wxT("song_artistid=");
                    break;

                case guLIBRARY_ELEMENT_COMPOSERS:
                    RetVal += wxT("song_composerid=");
                    break;

                case guLIBRARY_ELEMENT_ALBUMARTISTS:
                    RetVal += wxT("song_albumartistid=");
                    break;

                case guLIBRARY_ELEMENT_ALBUMS:
                    RetVal += wxT("song_albumid=");
                    break;

                case guLIBRARY_ELEMENT_YEARS:
                    RetVal += wxT("song_year=");
                    break;

                case guLIBRARY_ELEMENT_RATINGS:
                    RetVal += wxT("song_rating=");
                    break;

                case guLIBRARY_ELEMENT_PLAYCOUNT:
                    RetVal += wxT("song_playcount=");
                    break;
            }

            RetVal += wxString::Format(wxT("%i AND "), filterentry[Index].m_Id);
        }
        RetVal.RemoveLast(4);
        RetVal += wxT(")");
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    wxString inline GetTreeViewFilterArraySql(const guTreeViewFilterArray &filterarray)
    {
        wxString RetVal = wxT("(");
        int Count = filterarray.Count();
        for (int Index = 0; Index < Count; Index++)
        {
            RetVal += GetTreeViewFilterEntrySql(filterarray[Index]);
            RetVal += wxT("OR ");
        }
        RetVal.RemoveLast(3);
        RetVal += wxT(")");
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetSongsCounters(const guTreeViewFilterArray &filters, const wxArrayString &textfilters,
                                       wxLongLong *count, wxLongLong *len, wxLongLong *size)
    {
        if (!filters.Count())
        {
            *count = 0;
            *len = 0;
            *size = 0;
            return;
        }
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT COUNT(), SUM(song_length), SUM(song_filesize) FROM songs ");
        query += wxT("WHERE ") + GetTreeViewFilterArraySql(filters);
        if (textfilters.Count())
            query += wxT(" AND ") + TextFilterToSQL(textfilters);

        dbRes = (const wxSQLite3ResultSet) ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            *count = dbRes.GetInt64(0);
            *len = dbRes.GetInt64(1);
            *size = dbRes.GetInt64(2);
        }
        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongs(const guTreeViewFilterArray &filters, guTrackArray *Songs,
                              const wxArrayString &textfilters, const wxArrayInt order, const wxArrayInt orderdesc)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;

        query = GU_TRACKS_QUERYSTR;
        query += wxT("WHERE ") + GetTreeViewFilterArraySql(filters);
        if (textfilters.Count())
        {
            query += wxT(" AND ") + TextFilterToSQL(textfilters);
        }
        query += GetSongsSortSQL(&order, &orderdesc);

        //guLogMessage( wxT( "%s" ), query.c_str() );
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            Song = new guTrack();
            FillTrackFromDb(Song, &dbRes);
            Songs->Add(Song);
        }
        dbRes.Finalize();

        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongsCount()
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT COUNT(song_id) FROM songs ");
        if (GetFiltersCount())
        {
            //query += ( query.Find( wxT( "WHERE" ) ) == wxNOT_FOUND ) ? wxT( "WHERE " ) : wxT( "AND " );
            query += wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_SONGS);
        }
        //  query += GetSongsSortSQL( m_TracksMultiOrder, m_TracksMultiOrderDesc );

        //guLogMessage( wxT( "%s" ), query.c_str() );
        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
            return dbRes.GetInt(0);

        return 0;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongs(guTrackArray *Songs, const int start, const int end)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guTrack *Song;

        query = GU_TRACKS_QUERYSTR;
        if (GetFiltersCount())
        {
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += FiltersSQL(guLIBRARY_FILTER_SONGS);
        }
        query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc, true);
        query += wxString::Format(wxT(" LIMIT %i, %i "), start, end - start + 1);

        //guLogMessage( wxT( "%s" ), query.c_str() );
        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            Song = new guTrack();
            FillTrackFromDb(Song, &dbRes);
            Songs->Add(Song);
        }
        dbRes.Finalize();

        return Songs->Count();
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetSongsId(const int start)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT song_id FROM songs ");
        if (GetFiltersCount())
        {
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += FiltersSQL(guLIBRARY_FILTER_SONGS);
        }
        query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);
        query += wxString::Format(wxT(" LIMIT %i, 1 "), start);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            return dbRes.GetInt(0);

        return 0;
    }

    // -------------------------------------------------------------------------------- //
    wxString guDbLibrary::GetSongsName(const int start)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT song_name FROM songs ");
        if (GetFiltersCount())
        {
            query += (query.Find(wxT("WHERE")) == wxNOT_FOUND) ? wxT("WHERE ") : wxT("AND ");
            query += FiltersSQL(guLIBRARY_FILTER_SONGS);
        }
        query += GetSongsSortSQL(&m_TracksMultiOrder, &m_TracksMultiOrderDesc);
        query += wxString::Format(wxT(" LIMIT %i, 1 "), start);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            return dbRes.GetString(0);

        return wxEmptyString;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::GetTracksCounters(wxLongLong *count, wxLongLong *len, wxLongLong *size)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT COUNT(), SUM(song_length), SUM(song_filesize) FROM songs ");
        if (GetFiltersCount())
            query += wxT("WHERE ") + FiltersSQL(guLIBRARY_FILTER_SONGS);

        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
        {
            *count = dbRes.GetInt64(0);
            *len = dbRes.GetInt64(1);
            *size = dbRes.GetInt64(2);
        }
        dbRes.Finalize();
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::DeleteLibraryTracks(const guTrackArray *tracks, const bool createdeleted)
    {
        wxArrayInt Tracks;
        wxArrayInt Covers;
        int Count = tracks->Count();
        for (int Index = 0; Index < Count; Index++)
        {
            guTrack &Track = tracks->Item(Index);
            Tracks.Add(Track.m_SongId);

            if (createdeleted)
                FindDeletedFile(Track.m_FileName, true);
        }
        CleanItems(Tracks, Covers);
    }

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::FindDeletedFile(const wxString &file, const bool create)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        query = wxString::Format(wxT("SELECT delete_id FROM deleted WHERE delete_path = '%s' LIMIT 1;"),
                                 escape_query_str(file).c_str());
        dbRes = ExecuteQuery(query);

        if (dbRes.NextRow())
            return true;

        if (create)
        {
            query = wxT("INSERT INTO deleted( delete_id, delete_path, delete_date ) ");
            query += wxString::Format(wxT("VALUES(NULL, '%s', %lu )"),
                                      escape_query_str(file).c_str(), wxDateTime::GetTimeNow());
            ExecuteUpdate(query);
            return true;
        }
        return false;
    }

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::GetAlbumInfo(const int AlbumId, wxString *AlbumName, wxString *ArtistName, wxString *AlbumPath)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        bool RetVal = false;

        query = wxString::Format(wxT("SELECT DISTINCT song_album, song_albumartist, song_artist, song_path "
            "FROM songs WHERE song_albumid = %u LIMIT 1"), AlbumId);

        //guLogMessage( query );

        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
        {
            if (AlbumName)
                *AlbumName = dbRes.GetString(0);
            if (ArtistName)
            {
                *ArtistName = dbRes.GetString(1);
                if (ArtistName->IsEmpty())
                    *ArtistName = dbRes.GetString(2);
            }
            if (AlbumPath)
                *AlbumPath = dbRes.GetString(3);
            RetVal = true;
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetAlbumCoverId(const int AlbumId)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        int RetVal = wxNOT_FOUND;

        query = wxString::Format(wxT("SELECT song_coverid ")
                                 wxT("FROM songs ")
                                 wxT("WHERE song_albumid = %u LIMIT 1;"), AlbumId);

        dbRes = ExecuteQuery(query);
        if (dbRes.NextRow())
            RetVal = dbRes.GetInt(0);

        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    guArrayListItems guDbLibrary::GetArtistsLabels(const wxArrayInt &Artists)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guArrayListItems RetVal;
        guArrayListItem *CurItem = nullptr;
        int Id;
        int count = Artists.Count();
        if (count)
        {
            for (int index = 0; index < count; index++)
            {
                Id = Artists[index];
                RetVal.Add(new guArrayListItem(Id));
            }

            query = wxT("SELECT settag_artistid, settag_tagid FROM settags ")
                    wxT("WHERE settag_artistid IN ") + ArrayIntToStrList(Artists);
            query += wxT(" ORDER BY settag_artistid,settag_tagid;");

            dbRes = ExecuteQuery(query);

            int index = 0;
            CurItem = &RetVal[index];
            while (dbRes.NextRow())
            {
                Id = dbRes.GetInt(0);
                if (CurItem->GetId() != Id)
                {
                    index = 0;
                    while (CurItem->GetId() != Id)
                    {
                        CurItem = &RetVal[index];
                        index++;
                    }
                }
                CurItem->AddData(dbRes.GetInt(1));
            }
            dbRes.Finalize();
        }
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    guArrayListItems guDbLibrary::GetAlbumsLabels(const wxArrayInt &Albums)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guArrayListItems RetVal;
        guArrayListItem *CurItem = nullptr;
        int Id;
        int count = Albums.Count();
        for (int index = 0; index < count; index++)
        {
            Id = Albums[index];
            RetVal.Add(new guArrayListItem(Id));
        }

        query = wxT("SELECT settag_albumid, settag_tagid FROM settags ")
                wxT("WHERE settag_albumid IN ") + ArrayIntToStrList(Albums);
        query += wxT(" ORDER BY settag_albumid,settag_tagid;");

        dbRes = ExecuteQuery(query);

        int index = 0;
        CurItem = &RetVal[index];
        while (dbRes.NextRow())
        {
            Id = dbRes.GetInt(0);
            if (CurItem->GetId() != Id)
            {
                index = 0;
                while (CurItem->GetId() != Id)
                {
                    CurItem = &RetVal[index];
                    index++;
                }
            }
            CurItem->AddData(dbRes.GetInt(1));
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    guArrayListItems guDbLibrary::GetSongsLabels(const wxArrayInt &Songs)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guArrayListItems RetVal;
        guArrayListItem *CurItem = nullptr;
        int Id;
        int count = Songs.Count();
        for (int index = 0; index < count; index++)
        {
            Id = Songs[index];
            RetVal.Add(new guArrayListItem(Id));
        }

        query = wxT("SELECT settag_songid, settag_tagid FROM settags ")
                wxT("WHERE settag_songid IN ") + ArrayIntToStrList(Songs);
        query += wxT(" ORDER BY settag_songid,settag_tagid;");

        dbRes = ExecuteQuery(query);
        //guLogMessage( query );

        int index = 0;
        CurItem = &RetVal[index];
        while (dbRes.NextRow())
        {
            Id = dbRes.GetInt(0);
            if (CurItem->GetId() != Id)
            {
                index = 0;
                while (CurItem->GetId() != Id)
                {
                    CurItem = &RetVal[index];
                    index++;
                }
            }
            CurItem->AddData(dbRes.GetInt(1));
        }
        dbRes.Finalize();
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetArtistsLabels(const wxArrayInt &Artists, const wxArrayInt &Labels)
    {
        wxString query;
        int ArIndex;
        int ArCount;
        int LaIndex;
        int LaCount;

        query = wxT("DELETE FROM settags ")
                wxT("WHERE settag_artistid IN ") + ArrayIntToStrList(Artists);

        ExecuteUpdate(query);

        ArCount = Artists.Count();
        LaCount = Labels.Count();
        for (ArIndex = 0; ArIndex < ArCount; ArIndex++)
        {
            for (LaIndex = 0; LaIndex < LaCount; LaIndex++)
            {
                query = wxString::Format(wxT("INSERT INTO settags( settag_tagid, settag_artistid ) "\
                    "VALUES( %u, %u );"), Labels[LaIndex], Artists[ArIndex]);
                ExecuteUpdate(query);
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetAlbumsLabels(const wxArrayInt &Albums, const wxArrayInt &Labels)
    {
        wxString query;
        int AlIndex;
        int AlCount;
        int TaIndex;
        int TaCount;

        query = wxT("DELETE FROM settags ")
                wxT("WHERE settag_albumid IN ") + ArrayIntToStrList(Albums);

        ExecuteUpdate(query);

        AlCount = Albums.Count();
        for (AlIndex = 0; AlIndex < AlCount; AlIndex++)
        {
            TaCount = Labels.Count();
            for (TaIndex = 0; TaIndex < TaCount; TaIndex++)
            {
                query = wxString::Format(wxT("INSERT INTO settags( settag_tagid, settag_albumid ) "\
                    "VALUES( %u, %u );"), Labels[TaIndex], Albums[AlIndex]);
                ExecuteUpdate(query);
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetSongsLabels(const wxArrayInt &Songs, const wxArrayInt &Labels)
    {
        wxString query;
        int SoIndex;
        int SoCount;
        int LaIndex;
        int LaCount;

        query = wxT("DELETE FROM settags ")
                wxT("WHERE settag_songid IN ") + ArrayIntToStrList(Songs);

        ExecuteUpdate(query);

        SoCount = Songs.Count();
        LaCount = Labels.Count();
        for (SoIndex = 0; SoIndex < SoCount; SoIndex++)
        {
            for (LaIndex = 0; LaIndex < LaCount; LaIndex++)
            {
                query = wxString::Format(wxT("INSERT INTO settags( settag_tagid, settag_songid ) "\
                    "VALUES( %u, %u );"), Labels[LaIndex], Songs[SoIndex]);
                ExecuteUpdate(query);
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateArtistsLabels(const guArrayListItems &labelsets)
    {
        guListItems LaItems;
        guTrackArray Songs;
        guTrack *Song;
        wxString ArtistLabelStr;

        // The ArtistLabels string is the same for all songs so done out of the loop
        GetLabels(&LaItems, true);

        int ArIndex;
        int ArCount = labelsets.Count();
        for (ArIndex = 0; ArIndex < ArCount; ArIndex++)
        {
            wxArrayInt ArLabels = labelsets[ArIndex].GetData();
            int Count = ArLabels.Count();

            ArtistLabelStr.Empty();
            for (int Index = 0; Index < Count; Index++)
            {
                ArtistLabelStr += guListItemsGetName(LaItems, ArLabels[Index]);
                ArtistLabelStr += wxT("|");
            }
            if (!ArtistLabelStr.IsEmpty())
                ArtistLabelStr.RemoveLast(1);

            //guLogMessage( wxT( "Artist Labels : '%s'" ), ArtistLabelStr.c_str() );
            // Update the Database
            wxArrayInt Artists;
            Artists.Add(labelsets[ArIndex].GetId());
            SetArtistsLabels(Artists, ArLabels);

            if (m_MediaViewer->GetEmbeddMetadata())
            {
                // Get the affected tracks
                GetArtistsSongs(Artists, &Songs);
                Count = Songs.Count();
                for (int Index = 0; Index < Count; Index++)
                {
                    Song = &Songs[Index];

                    if (wxFileExists(Song->m_FileName))
                    {
                        guTagInfo *TagInfo;
                        TagInfo = guGetTagInfoHandler(Song->m_FileName);
                        if (!TagInfo)
                            continue;

                        TagInfo->Read();
                        TagInfo->m_ArtistLabelsStr = ArtistLabelStr;
                        TagInfo->Write(guTRACK_CHANGED_DATA_LABELS);

                        delete TagInfo;
                    } else
                        guLogError(wxT("The file '%s' could not be found"), Song->m_FileName.c_str());
                }
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateAlbumsLabels(const guArrayListItems &labelsets)
    {
        guListItems LaItems;
        guTrackArray Songs;
        guTrack *Song;
        wxString AlbumLabelStr;

        // The ArtistLabels string is the same for all songs so done out of the loop
        GetLabels(&LaItems, true);

        int ItemIndex;
        int ItemCount = labelsets.Count();
        for (ItemIndex = 0; ItemIndex < ItemCount; ItemIndex++)
        {
            wxArrayInt ItemLabels = labelsets[ItemIndex].GetData();
            int Count = ItemLabels.Count();

            AlbumLabelStr.Empty();
            for (int Index = 0; Index < Count; Index++)
            {
                AlbumLabelStr += guListItemsGetName(LaItems, ItemLabels[Index]) + wxT("|");
            }
            if (!AlbumLabelStr.IsEmpty())
                AlbumLabelStr.RemoveLast(1);

            guLogMessage(wxT("Album Labels : '%s'"), AlbumLabelStr.c_str());
            // Update the Database
            wxArrayInt ItemIds;
            ItemIds.Add(labelsets[ItemIndex].GetId());
            SetAlbumsLabels(ItemIds, ItemLabels);

            if (m_MediaViewer->GetEmbeddMetadata())
            {
                // Get the affected tracks
                GetAlbumsSongs(ItemIds, &Songs);

                Count = Songs.Count();
                for (int Index = 0; Index < Count; Index++)
                {
                    Song = &Songs[Index];

                    if (wxFileExists(Song->m_FileName))
                    {
                        guTagInfo *TagInfo;
                        TagInfo = guGetTagInfoHandler(Song->m_FileName);
                        if (!TagInfo)
                            continue;

                        TagInfo->Read();
                        TagInfo->m_AlbumLabelsStr = AlbumLabelStr;
                        TagInfo->Write(guTRACK_CHANGED_DATA_LABELS);

                        delete TagInfo;
                    } else
                        guLogError(wxT("The file '%s' could not be found"), Song->m_FileName.c_str());
                }
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateSongsLabels(const guArrayListItems &labelsets)
    {
        guListItems LaItems;
        guTrackArray Songs;
        guTrack *Song;
        wxString TrackLabelStr;

        // The ArtistLabels string is the same for all songs so done out of the loop
        GetLabels(&LaItems, true);

        int ItemIndex;
        int ItemCount = labelsets.Count();
        for (ItemIndex = 0; ItemIndex < ItemCount; ItemIndex++)
        {
            wxArrayInt ItemLabels = labelsets[ItemIndex].GetData();
            int Count = ItemLabels.Count();

            TrackLabelStr.Empty();
            for (int Index = 0; Index < Count; Index++)
                TrackLabelStr += guListItemsGetName(LaItems, ItemLabels[Index]) + wxT("|");

            if (!TrackLabelStr.IsEmpty())
                TrackLabelStr.RemoveLast(1);

            //guLogMessage( wxT( "Artist Labels : '%s'" ), ArtistLabelStr.c_str() );
            // Update the Database
            wxArrayInt ItemIds;
            ItemIds.Add(labelsets[ItemIndex].GetId());
            SetSongsLabels(ItemIds, ItemLabels);

            if (m_MediaViewer->GetEmbeddMetadata())
            {
                // Get the affected tracks
                GetSongs(ItemIds, &Songs);

                Count = Songs.Count();
                for (int Index = 0; Index < Count; Index++)
                {
                    Song = &Songs[Index];

                    if (Song->m_Offset)
                        continue;

                    if (wxFileExists(Song->m_FileName))
                    {
                        guTagInfo *TagInfo;
                        TagInfo = guGetTagInfoHandler(Song->m_FileName);
                        if (!TagInfo)
                            continue;

                        TagInfo->Read();
                        TagInfo->m_TrackLabelsStr = TrackLabelStr;
                        TagInfo->Write(guTRACK_CHANGED_DATA_LABELS);

                        delete TagInfo;
                    } else
                        guLogError(wxT("The file '%s' could not be found"), Song->m_FileName.c_str());
                }
            }
        }
    }

    // -------------------------------------------------------------------------------- //
    void inline RemoveLabel(wxString *labelstr, const wxString *labelname, const wxString *newname)
    {
        if (labelstr->IsEmpty())
            return;
        if (labelstr->Replace(wxT("|" + ( * labelname )), *newname))
            return;
        if (labelstr->Replace((*labelname) + wxT("|"), *newname))
            return;
        labelstr->Replace((*labelname), *newname);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateSongLabel(const guTrack &track, const wxString &label, const wxString &newname)
    {
        if (wxFileExists(track.m_FileName))
        {
            guTagInfo *TagInfo = guGetTagInfoHandler(track.m_FileName);
            if (!TagInfo)
                return;

            TagInfo->Read();

            RemoveLabel(&TagInfo->m_TrackLabelsStr, &label, &newname);
            RemoveLabel(&TagInfo->m_ArtistLabelsStr, &label, &newname);
            RemoveLabel(&TagInfo->m_AlbumLabelsStr, &label, &newname);

            TagInfo->Write(guTRACK_CHANGED_DATA_LABELS);

            delete TagInfo;
        } else
            guLogError(wxT("The file '%s' could not be found"), track.m_FileName.c_str());
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateSongsLabel(const guTrackArray &tracks, const wxString &label, const wxString &newname)
    {
        int Count = tracks.Count();
        for (int Index = 0; Index < Count; Index++)
            UpdateSongLabel(tracks[Index], label, newname);
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetTrackRating(const int songid, const int rating, const bool writetags)
    {
        wxString query;
        query = wxString::Format(wxT("UPDATE songs SET song_rating = %u WHERE song_id = %u;"),
                                 rating, songid);

        ExecuteUpdate(query);

        if (writetags) {}
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetTracksRating(const wxArrayInt &songids, const int rating, const bool writetags)
    {
        wxString query;
        if (songids.Count())
        {
            query = wxString::Format(wxT("UPDATE songs SET song_rating = %u WHERE song_id IN "),
                                     rating);
            query += ArrayIntToStrList(songids);
            ExecuteUpdate(query);

            if (writetags) {}
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetTracksRating(const guTrackArray *tracks, const int rating, const bool writetags)
    {
        int Count = tracks->Count();
        if (Count)
        {
            wxArrayInt TrackIds;
            for (int Index = 0; Index < Count; Index++)
                TrackIds.Add(tracks->Item(Index).m_SongId);

            SetTracksRating(TrackIds, rating, writetags);
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::SetTrackPlayCount(const int songid, const int playcount, const bool writetags)
    {
        wxString query;
        query = wxString::Format(wxT("UPDATE songs SET song_playcount = %u, song_lastplay = %lu WHERE song_id = %u;"),
                                 playcount, wxDateTime::GetTimeNow(), songid);
        ExecuteUpdate(query);

        if (writetags) {}
    }

    // -------------------------------------------------------------------------------- //
    int guDbLibrary::GetEmptyCovers(guCoverInfos &coverinfos)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;

        query = wxT("SELECT DISTINCT song_albumid, song_album, song_artist, song_path "
            "FROM songs WHERE song_coverid ISNULL OR song_coverid < 1 GROUP BY song_albumid");

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            coverinfos.Add(new guCoverInfo(dbRes.GetInt(0), dbRes.GetString(1), dbRes.GetString(2),
                                           dbRes.GetString(3)));
        }
        dbRes.Finalize();

        return coverinfos.Count();
    }

    wxString guDbLibrary::TextFilterToSQL(const wxArrayString &TeFilters)
    {
        int count = TeFilters.Count();
        bool collation_enabled = m_MediaViewer->GetCollationSearch();
        wxString RetVal;

        if (count)
        {
            for (int index = 0; index < count; index++)
            {
                wxString Filter = escape_query_str(TeFilters[index]);
                RetVal += "(" + Contains_Or_Like("song_name", Filter, collation_enabled) + "OR ";
                RetVal += Contains_Or_Like("song_artist", Filter, collation_enabled) + "OR ";
                RetVal += Contains_Or_Like("song_album", Filter, collation_enabled) + "OR ";
                RetVal += Contains_Or_Like("song_albumartist", Filter, collation_enabled) + "OR ";
                RetVal += Contains_Or_Like("song_composer", Filter, collation_enabled);
                RetVal += wxT(") AND ");
            }
            RetVal = RetVal.RemoveLast(4);
        }
        return RetVal;
    }

    wxString guDbLibrary::AlbumBrowserTextFilterToSQL(const wxArrayString &textfilters)
    {
        int count = textfilters.Count();
        bool collation_enabled = m_MediaViewer->GetCollationSearch();
        wxString RetVal;

        if (count)
        {
            for (int index = 0; index < count; index++)
            {
                wxString Filter = escape_query_str(textfilters[index]);
                RetVal += "(" + Contains_Or_Like("song_artist", Filter, collation_enabled) + "OR ";
                RetVal += Contains_Or_Like("song_album", Filter, collation_enabled) + "OR ";
                RetVal += Contains_Or_Like("song_albumartist", Filter, collation_enabled);
                RetVal += wxT(") AND ");
            }
            RetVal = RetVal.RemoveLast(4);
        }
        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    // AudioScrobbler Functions
    // -------------------------------------------------------------------------------- //
    //audioscs(
    //    audiosc_id INTEGER PRIMARY KEY AUTOINCREMENT,
    //    audiosc_artist VARCHAR(255),
    //    audiosc_album varchar(255),
    //    audiosc_track varchar(255),
    //    audiosc_playedtime INTEGER,
    //    audiosc_source char(1),
    //    audiosc_ratting char(1),
    //    audiosc_len INTEGER,
    //    audiosc_tracknum INTEGER,
    //    audiosc_mbtrackid INTEGER
    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::AddCachedPlayedSong(const guCurrentTrack &Song)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        wxString Title = Song.m_SongName;
        wxString Artist = Song.m_ArtistName;
        wxString Album = Song.m_AlbumName;
        char Source;

        if (Song.m_Type == guTRACK_TYPE_RADIOSTATION)
            Source = 'R';
        else
        {
            switch (Song.m_TrackMode)
            {
                case guTRACK_MODE_SMART:
                    Source = 'L';
                    break;
                case guTRACK_MODE_USER:
                    Source = 'P';
                    break;
                case guTRACK_MODE_RANDOM:
                    Source = 'S';
                    break;
                default:
                    Source = 'U';
                    break;
            }
        }

        char Rating;
        switch (Song.m_ASRating)
        {
            case guAS_RATING_LOVE:
                Rating = 'L';
                break;
            case guAS_RATING_BAN:
                Rating = 'B';
                break;
            case guAS_RATING_SKIP:
                Rating = 'S';
                break;
            //case guAS_RATING_NONE :
            default:
                Rating = ' ';
                break;
        }

        escape_query_str(&Title);
        escape_query_str(&Artist);
        escape_query_str(&Album);

        int PlayTime = Song.m_PlayTime / 1000;
        int TrackLength = Song.m_Length / 1000;

        if (Song.m_Type == guTRACK_TYPE_RADIOSTATION)
        {
            PlayTime = 180;
            TrackLength = 180;
        }

        guLogMessage(wxT("Adding track at %lu played %u mseconds"), wxGetUTCTime(), PlayTime);

        query = wxString::Format(wxT("INSERT into audioscs( audiosc_id, audiosc_artist, "
            "audiosc_album, audiosc_track, audiosc_playedtime, audiosc_source, "
            "audiosc_ratting, audiosc_len, audiosc_tracknum, audiosc_mbtrackid) "
            "VALUES(NULL, '%s', '%s', '%s', %lu, '%c', '%c', %u, %i, %u );"),
                                 Artist.c_str(),
                                 Album.c_str(),
                                 Title.c_str(),
                                 wxGetUTCTime() - PlayTime,
                                 Source,
                                 Rating,
                                 TrackLength,
                                 Song.m_Number,
                                 0);
        //guLogMessage( query );
        return ExecuteUpdate(query);
    }

    // -------------------------------------------------------------------------------- //
    guAS_SubmitInfoArray guDbLibrary::GetCachedPlayedSongs(int MaxCount)
    {
        wxString query;
        wxSQLite3ResultSet dbRes;
        guAS_SubmitInfoArray RetVal;
        guAS_SubmitInfo *PlayedSong;

        query = wxString::Format(wxT("SELECT audiosc_id, audiosc_artist, audiosc_album, audiosc_track, "
            "audiosc_playedtime, audiosc_source, audiosc_ratting, audiosc_len, "
            "audiosc_tracknum, audiosc_mbtrackid "
            "FROM audioscs ORDER BY audiosc_playedtime LIMIT %u"), MaxCount);

        dbRes = ExecuteQuery(query);

        while (dbRes.NextRow())
        {
            PlayedSong = new guAS_SubmitInfo();
            PlayedSong->m_Id = dbRes.GetInt(0);
            PlayedSong->m_ArtistName = dbRes.GetString(1);
            PlayedSong->m_AlbumName = dbRes.GetString(2);
            PlayedSong->m_TrackName = dbRes.GetString(3);
            PlayedSong->m_PlayedTime = dbRes.GetInt(4);
            PlayedSong->m_Source = dbRes.GetString(5)[0];
            PlayedSong->m_Rating = dbRes.GetString(6)[0];
            PlayedSong->m_TrackLen = dbRes.GetInt(7);
            PlayedSong->m_TrackNum = dbRes.GetInt(8);
            PlayedSong->m_MBTrackId = dbRes.GetInt(9);
            RetVal.Add(PlayedSong);
        }
        dbRes.Finalize();

        return RetVal;
    }

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::DeleteCachedPlayedSongs(const guAS_SubmitInfoArray &SubmitInfo)
    {
        wxString query;
        //  wxSQLite3ResultSet dbRes;
        int count = SubmitInfo.Count();
        if (count)
        {
            query = wxT("DELETE from audioscs WHERE audiosc_id IN (");
            for (int index = 0; index < count; index++)
                query += wxString::Format(wxT("%u,"), SubmitInfo[index].m_Id);

            query.RemoveLast(1);
            query += wxT(");");

            if (ExecuteUpdate(query))
                return true;
        }
        return false;
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdateTrackFileName(const wxString &oldname, const wxString &newname)
    {
        int TrackId = FindTrackFile(oldname, nullptr);
        //guLogMessage( wxT( "The track %s was found with id %i" ), oldname.c_str(), TrackId );
        if (TrackId)
        {
            wxString NewPath = GetPathAddTrailSep(wxPathOnly(newname));
            int PathId = GetPathId(NewPath);

            wxString query = wxString::Format(
                wxT("UPDATE songs SET song_filename = '%s', song_pathid = %i, song_path = '%s' WHERE song_id = %u;"),
                escape_query_str(newname.AfterLast('/')).c_str(),
                PathId,
                escape_query_str(NewPath).c_str(),
                TrackId);

            //guLogMessage( wxT( "Updating file: %s" ), query.c_str() );
            ExecuteUpdate(query);
        }
    }

    // -------------------------------------------------------------------------------- //
    void guDbLibrary::UpdatePaths(const wxString &oldpath, const wxString &newpath)
    {
        wxString query;

        query = wxString::Format(wxT("UPDATE songs SET song_path = replace( song_path, '%s', '%s' )"),
                                 escape_query_str(oldpath).c_str(), escape_query_str(newpath).c_str());

        //guLogMessage( wxT( "Updating path: %s" ), query.c_str() );
        ExecuteUpdate(query);
    }

    // -------------------------------------------------------------------------------- //
    bool guDbLibrary::DirectoryHasPath()
    {
        return (!m_DiFilters.IsEmpty() && m_DiFilters != wxT(GU_COLLECTION_DUMMY_ROOTDIR));
    }
}
