/*****************************************************************************
Open Source MIT License

Copyright (c) 2017 Matthew Ahmed

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*****************************************************************************/

#include ".\songsselection.h"

// Pointer args EXCEPT naRepCounts6 allocated (new) by caller; this class deletes.
CSongsSelection::CSongsSelection(INT nType, INT * naRepCounts6, INT nMaxAge,
                                 BOOL bDedup, LPTSTR sSelectionName, // LPTSTR sSelectionDisplay,
                                 INT * nArr_LbSelectedIndexes, INT nLbSelectedArraySize,
                                 INT * nArr_PlistIndexes, LPTSTR * spArr_PlistNames, INT nPlistArraySize,
                                 LPTSTR * spArr_GenreNames, INT nGenreArraySize)
{
  m_nType = nType;
  for (int i = 0; i < 6; i++) m_naRepCounts[i] = naRepCounts6[i];
  m_nMaxAge = nMaxAge; // 0 for no max age, -1 if this is an exclusion list
  m_bDedup = bDedup;
  m_sSelectionName = sSelectionName;
//  m_sSelectionDisplay = sSelectionDisplay;
  // These are all 0 when nType == SEL_TYPE_ALL:
  m_nArr_LbSelectedIndexes = nArr_LbSelectedIndexes;
  m_nLbSelectedArraySize = nLbSelectedArraySize;
  m_nArr_PlistIndexes = nArr_PlistIndexes;
  m_spArr_PlistNames = spArr_PlistNames;
  m_nPlistArraySize = nPlistArraySize;
  m_spArr_GenreNames = spArr_GenreNames;
  m_nGenreArraySize = nGenreArraySize;

  // For generating tooltips
  m_nLenSelectionName = _tcslen(m_sSelectionName);
  m_sHdrGenres = _T("\r\n\r\nGenres:"); // Headers preceded by blank line
  m_sHdrPlists = _T("\r\n\r\nPlaylists:"); 
  m_nLenHdrGenres = _tcslen(m_sHdrGenres);
  m_nLenHdrPlists = _tcslen(m_sHdrPlists);
  m_sFmtTruncGenres = _T("\r\n[and %d genres not shown]");
  m_sFmtTruncPlists = _T("\r\n[and %d playlists not shown]");
  m_nLenTruncGenres = _tcslen(m_sFmtTruncGenres) + 2; // Allow up to 4 digits in %d
  m_nLenTruncPlists = _tcslen(m_sFmtTruncPlists) + 2;

//  if (m_sSelectionDisplay) delete [] m_sSelectionDisplay; // TESTING before remove from constructor
//  m_sSelectionDisplay = GetTooltipStr(1023); // 1023 is normal max for default tooltip

  if (nLbSelectedArraySize > 1)
  {
    m_sTooltipDefault = GetTooltipStr(1023); // 1023 is normal max for default tooltip
    m_nLenTooltipDefault = _tcslen(m_sTooltipDefault); // Usually shorter than max allowed
  }
  else // Leave ListView's handling of the tooltip if only 1 item selected 
  {
    m_sTooltipDefault = NULL;
    m_nLenTooltipDefault = 0;
  }
  m_sTooltipDynamic = NULL; // Create new if asked for tooltip shorter than default
}

CSongsSelection::~CSongsSelection(void)
{
  if (m_sSelectionName != NULL) delete [] m_sSelectionName;
//  if (m_sSelectionDisplay != NULL) delete [] m_sSelectionDisplay;
  if (m_sTooltipDefault != NULL) delete [] m_sTooltipDefault;
  if (m_sTooltipDynamic != NULL) delete [] m_sTooltipDynamic;
  if (m_nArr_LbSelectedIndexes != NULL) delete [] m_nArr_LbSelectedIndexes;
  if (m_nArr_PlistIndexes != NULL) delete [] m_nArr_PlistIndexes;

  if (m_spArr_GenreNames != NULL)
  {
    for (INT i = 0; i < m_nGenreArraySize; i++)
    {
      if (m_spArr_GenreNames[i] != NULL) delete [] m_spArr_GenreNames[i];
    }
    delete [] m_spArr_GenreNames;
  }

  if (m_spArr_PlistNames != NULL)
  {
    for (INT i = 0; i < m_nPlistArraySize; i++)
    {
      if (m_spArr_PlistNames[i] != NULL) delete [] m_spArr_PlistNames[i];
    }
    delete [] m_spArr_PlistNames;
  }
}

// protected version. Use wrapper for public
LPTSTR CSongsSelection::GetTooltipStr(INT nMaxLen) // nMaxLen normally 1023; always?
{
  // Returns NULL on fail, or string allocated by new that caller must delete.
  // First calculate how long list is with no truncation
  LPCTSTR sRN = _T("\r\n");
  INT nLenGenres;
  INT nLenPlists;

  // Length of genres list, no \r\n after last line
  if (!m_nGenreArraySize) nLenGenres = 0;
  else
  {
    // Will only have headers if have both genres and playlists
    if (m_nPlistArraySize) nLenGenres = m_nLenHdrGenres; // "GENRES:"
    else nLenGenres = 2; // "\r\n" instead of "\r\n\r\nGENRES:"

    for (INT i = 0; i < m_nGenreArraySize; i++)
      nLenGenres += (_tcslen(m_spArr_GenreNames[i]) + 2); // 2 for insert \r\n at start
  } // else, i.e. m_nGenreArraySize > 0

  // Length of playlists list, no \r\n after last line
  if (!m_nPlistArraySize) nLenPlists = 0;
  else
  {
    if (m_nGenreArraySize) nLenPlists = m_nLenHdrPlists; // "PLAYLISTS:"
    else nLenPlists = 2;

    for (INT i = 0; i < m_nPlistArraySize; i++)
      nLenPlists += (_tcslen(m_spArr_PlistNames[i]) + 2); // 2 for insert \r\n at start
  } // else, i.e. m_nGenreArraySize > 0

  // At least one of the list lengths is > 0, maybe both list.
  INT nLenAll = m_nLenSelectionName + nLenGenres + nLenPlists;

// Now all line breaks are included in the constants & their length vars
//  if (nLenGenres && nLenPlists) nLenAll += 4;  // double "\r\n" between lists

  if (nLenAll <= nMaxLen) // No need to truncate
  {
    LPTSTR sRet = new TCHAR[nLenAll + 1];
    if (!sRet) return NULL;
//    sRet[0] = _T('\0'); // to use as target in strcat
    _tcscpy(sRet, m_sSelectionName);

    // If both playlists and genres, arbitrarily show playlists first
    if (nLenPlists) // i.e. some Playlists in the selection
    {
      // If listing both genres and playlists, headers to show which are which.
      // Otherwise doesn't add any info to selection name (e.g. "3 playlists")
      if (nLenGenres) _tcscat(sRet, m_sHdrPlists);
//      else _tcscat(sRet, sRN);

      StrCatArrToStr(sRet, nLenAll, (LPCTSTR *) m_spArr_PlistNames, m_nPlistArraySize, sRN);
    }

// Now all line breaks are included in the constants & their length vars
//    // If return string has both lists, insert \r\n between lists
//    if (nLenGenres && nLenPlists) _tcscat(sRet, sRN); 

    if (nLenGenres) // i.e. some Genres in the selection
    {
      if (nLenPlists) _tcscat(sRet, m_sHdrGenres);
      else _tcscat(sRet, sRN);

      StrCatArrToStr(sRet, nLenAll, (LPCTSTR *) m_spArr_GenreNames, m_nGenreArraySize, sRN);
    }

    return sRet; // Caller must delete
  } // if (nLenAll <= nMaxLen)

  // If here, nLenAll > nMaxLen; much messier to generate string with trunctated list(s)

  // Just return NULL if nMaxLen too small to do much useful.
  // Need room to show at least the first of each playlists and/or genres.
 
  INT nMinimal = m_nLenSelectionName;
  // One or both Genre/Playlist conditions below true, adds to minumum requirement
  if (m_nPlistArraySize) // nMinimal += (m_nLenHdrPlists + m_nLenTruncPlists + 50)
  {
    if (m_nGenreArraySize) nMinimal += m_nLenHdrPlists; // Header if both genres/playlists
    else nMinimal += 2; // blank line if only playlists

    nMinimal += (_tcslen(m_spArr_GenreNames[0]) + 2); // Insert \r\n before genre name
    if (m_nPlistArraySize > 1) nMinimal += m_nLenTruncPlists; // else no trunc note
  }
  if (m_nGenreArraySize) //  nMinimal += (m_nLenHdrGenres + m_nLenTruncGenres + 30);
  {
    if (m_nPlistArraySize) nMinimal += m_nLenHdrGenres; 
    else nMinimal += 2;

    nMinimal += (_tcslen(m_spArr_GenreNames[0]) + 2);
    if (m_nGenreArraySize > 1) nMinimal += m_nLenTruncGenres; // else no trunc note
  }

  if (nMaxLen < nMinimal) return NULL;

  // If both genres and playlists, will list playlists first.
  // Set max allowed space for playlists, then genres will use remainder of nMaxLen.
  INT nMaxLenPlists;
  if (nLenPlists) // For boolean existence, either m_nPlistArraySize or nLenPlists
  {
    if (nLenGenres) // Have both playlists & genres.
    {
      // Figure playlists on average have longer names, give .6 of total string.
      // But if genres fit w/o truncation in less than .4, give playlist all
      // the space (more than .6) that will not be used for genres.
      if (nLenGenres < (INT) (.4 * nMaxLen)) nMaxLenPlists = nMaxLen - nLenGenres;
      else nMaxLenPlists = (INT) (.6 * nMaxLen); // May not need/use all of this
    }
    else nMaxLenPlists = nMaxLen; // Have playlists only
  }
  // Don't need similar nMaxLenGenres variable.
  // After putting playlists (if any) in return str, genres use all remaining nMaxLen.

  LPTSTR sRet = new TCHAR[nMaxLen + 1]; // Might not use quite all of this
  if (!sRet) return NULL;
//  sRet[0] = _T('\0'); // to use as target in strcat
  _tcscpy(sRet, m_sSelectionName);

  TCHAR sTruncNote[200];    // More than will be needed

  if (m_nPlistArraySize)
  {
    if (m_nGenreArraySize) // Have both genres & playlists; headers to distinguish
      _tcscat(sRet, m_sHdrPlists);
    else _tcscat(sRet, sRN); // Playlists only; just blank line after selection name


    // Initially copy however much of playlists array fits, may subsequently trim back.
    INT nCopied = StrCatArrToStr(sRet, nMaxLenPlists, (LPCTSTR *) m_spArr_PlistNames,
                                 m_nPlistArraySize, sRN);

    if (nCopied < m_nPlistArraySize)
    {
      // Not all playlist names were copied (always true if no genres)
      // Will append truncation note about how many playlists not shown.
      _stprintf(sTruncNote, m_sFmtTruncPlists, m_nPlistArraySize - nCopied);
      INT nLenNote = _tcslen(sTruncNote);

      // May or may not have enough space to append truncation note.
      // If not, trim off one or more playlists names until there is enough space.
      while ((INT) _tcslen(sRet) + nLenNote > nMaxLenPlists) // Might not enter loop
      {
        // Each playlist preceded by "\r\n", no \r\n after last playlist
        LPTSTR spLastRN = _tcsrchr(sRet, _T('\r'));
        if (!spLastRN) return NULL; // Should never happen. Give up if it does.
        spLastRN[0] = _T('\0'); // Truncate string, dropping last playlist
      }

      _tcscat(sRet, sTruncNote); // sTruncNote starts with \r\n, has none at end.
    } // if (nCopied < m_nPlistArraySize)  // Can happen whether genres or not
    // else list of playlists not truncted (Must have genres, will truncate that)

// Now all line breaks are included in the constants & their length vars
//    // sRet doesn't have ending \r\n. Need line break if followed by genres.
//    if (m_nGenreArraySize) _tcscat(sRet, sRN);
  } // if (m_nPlistArraySize), i.e. copy some or all playlists to return string

  if (m_nGenreArraySize)
  {
    // Have genres. Handling is same regardless whether sRet empty or has playlists.
    // How much to copy is controlled by how much unused space is in nMaxLen string.
    if (m_nPlistArraySize) _tcscat(sRet, m_sHdrGenres);
    else _tcscat(sRet, sRN);

    INT nCopied = StrCatArrToStr(sRet, nMaxLen, (LPCTSTR *) m_spArr_GenreNames,
                                 m_nGenreArraySize, sRN);

    if (nCopied < m_nGenreArraySize)
    {
      // Not all genre names were copied (always true if no playlists)
      _stprintf(sTruncNote, m_sFmtTruncGenres, m_nGenreArraySize - nCopied);
      INT nLenNote = _tcslen(sTruncNote);

      while ((INT) _tcslen(sRet) + nLenNote > nMaxLen) // Might not enter loop
      {
        // Each genre preceded by "\r\n", no \r\n after last playlist
        LPTSTR spLastRN = _tcsrchr(sRet, _T('\r'));
        if (!spLastRN) return NULL;
        spLastRN[0] = _T('\0');
      }

      _tcscat(sRet, sTruncNote); 
    } // if (nCopied < m_nPGenreArraySize)
    // else all genres were copied (must also have playlists, truncated)
  } // if (m_nGenresArraySize)

  return sRet; // Caller must delete
} // LPTSTR CSongsSelection::GetTooltipStr(INT nMaxLen)

INT CSongsSelection::GetType(void)
{
  return m_nType;
}

BOOL CSongsSelection::IsAllSongs(void) // else SEL_TYPE_LISTS, playlist(s)/genre(s)
{
  return (m_nType == SEL_TYPE_ALL);
}

const INT * CSongsSelection::GetRepCounts()
{
  return (const INT *) m_naRepCounts;
}

BOOL CSongsSelection::IsExclude(void)
{
  return (m_naRepCounts[0] == -1);
}

BOOL CSongsSelection::IsDedup(void)
{
  return m_bDedup;
}

INT CSongsSelection::GetMaxAge(void)
{
  return m_nMaxAge; // 0 if no age limit
}

LPCTSTR CSongsSelection::GetSelectionName()
{
  return (LPCTSTR) m_sSelectionName;
}

INT CSongsSelection::GetSelectionNameLen()
{
  return _tcslen(m_sSelectionName);
}

LPCTSTR CSongsSelection::GetSelectionDisplay(INT nMaxLen)
{
//  return (LPCTSTR) m_sSelectionDisplay;
  // Tooltip previously created in constructor almost always less than nMaxLen
  if (nMaxLen >= m_nLenTooltipDefault) return m_sTooltipDefault;
  else // Need to generate a new tooltip with unusually short nMaxLen
  {
    if (m_sTooltipDynamic) delete [] m_sTooltipDynamic;
    m_sTooltipDynamic = GetTooltipStr(nMaxLen);
    return m_sTooltipDynamic;
  }
}

//INT CSongsSelection::GetSelectionDisplayLen()
//{
////  return _tcslen(m_sSelectionDisplay);
//  return m_nLenTooltipDefault;
//}

const INT * CSongsSelection::GetLbSelectedIndexesArray()
{
  return (const INT *) m_nArr_LbSelectedIndexes;
}

INT CSongsSelection::GetLbSelectedIndexesCount()
{
  return m_nLbSelectedArraySize;
}

LPCTSTR * CSongsSelection::GetGenreNamesArray()
{
  return (LPCTSTR *) m_spArr_GenreNames;
}

INT CSongsSelection::GetGenreNamesCount()
{
  return m_nGenreArraySize;
}

const INT * CSongsSelection::GetPlistIndexesArray()
{
  return (const INT *) m_nArr_PlistIndexes;
}

INT CSongsSelection::GetPlistIndexesCount()
{
  return m_nPlistArraySize;
}



