//--------------------------------------------------------------
//  FileSelector class
//      SD カード内の *.bmp ファイル名の一覧を表示し，ファイルを選択する
//      だだし，日本語のファイル名は除く
//
//  2018/10/23, Copyright (c) 2018 MIKAMI, Naoki
//--------------------------------------------------------------

#include "FileSelectorBmp.hpp"

namespace Mikami
{
    FileSelector::FileSelector(uint8_t x0, uint8_t y0, int maxFiles,
                               int maxNameLength, SD_BitmapReader &reader)
        : X_(x0), Y_(y0),
          MAX_FILES_(maxFiles), MAX_NAME_LENGTH_(maxNameLength),
          fileNames_(maxFiles), fileNameLabels_(MAX_LIST_PAGE_),
          next_(50, 235, 64, 36, 2, (string[]){"<", ">"}, 10, 0, 2, -1, Font24),
          pageLabel_(210, 250, Label::LEFT, Font16),
          lcd_(GuiBase::GetLcd()), sdReader_(reader), page_(1),
          nOld_(-1), kOld_(-1)
    {
        Array<string> nonString(MAX_LIST_PAGE_, "");
        rect_ = new ButtonGroup(X_, Y_, W_H_, W_H_, MAX_LIST_PAGE_,
                                nonString, 0, V_L_-W_H_, 1,
                                -1, Font12, 0, GuiBase::ENUM_BACK,
                                BASE_COLOR_, TOUCHED_COLOR_);
        rect_->EraseAll();
        next_.EraseAll();

        // Label を生成
        for (int n=0; n<MAX_LIST_PAGE_; n++)
            fileNameLabels_[n] = new Label(X_+30, Y_+5+V_L_*n, "",
                                           Label::LEFT, Font16, BASE_COLOR_);
    }

    FileSelector::~FileSelector()
    {   
        delete rect_;
        for (int n=0; n<MAX_LIST_PAGE_; n++) delete fileNameLabels_[n];
    }

    // ファイルの一覧表を作る．BMP ファイルが存在しない場合は false を返す
    bool FileSelector::CreateTable()
    {
        DIR* dp = opendir("/sd");
        fileCount_ = 0; 
        if (dp != NULL)
        {
            dirent* entry;
            for (int n=0; n<256; n++)
            {
                entry = readdir(dp);
                if (entry == NULL) break;

                string strName = entry->d_name;
                if ( (strName.find(".bmp") != string::npos) ||
                     (strName.find(".BMP") != string::npos) )
                {
                    if (sdReader_.Open(strName))        // ファイルのオープン
                    {
                        // 不適切なファイルは除外
                        if (sdReader_.IsAllowed())
                            fileNames_[fileCount_++] = strName;
                        sdReader_.Close();
                    }
                }
                if (fileCount_ >= MAX_FILES_) break;
            }
            closedir(dp);
        }
        else
            return false;                   // SD カードが装着されていない場合
        if (fileCount_ == 0) return false;  // 該当する BMP ファイルが存在しない場合

        div_t m = div(fileCount_, MAX_LIST_PAGE_);
        maxPage_ = (m.rem == 0) ? m.quot : m.quot+1;
        return true;                        // 該当する BMP ファイルが存在する場合
    }

    // ファイルを選択する
    bool FileSelector::Select(string &fileName)
    {
        int m;
        if (next_.GetTouchedNumber(m))
        {
            if (m==0) page_--;
            else      page_++;
            DisplayFileList(false);
            nOld_ = -1;
            wait_ms(300);
        }
        
        int n;
        if (rect_->GetTouchedNumber(n))
        {
            int k = (page_ - 1)*MAX_LIST_PAGE_ + n;
            fileNameLabels_[n]->Draw(GetFileNameNoExt(k),
                                     TOUCHED_COLOR_);
            fileName = fileNames_[k];
            // 同じページで以前に選択されているファイル名の色を戻す
            if (nOld_ != -1)
                fileNameLabels_[nOld_]->Draw(GetFileNameNoExt(kOld_),
                                             BASE_COLOR_);
            if (page_ == maxPage_)      // 最後のページで余分な四角形を消去
                for (int j=fileCount_ % MAX_LIST_PAGE_ + 1;
                     j < MAX_LIST_PAGE_; j++) rect_->Erase(j);
            nOld_ = n;
            kOld_ = k;
            return true;
        }
        else
            return false;
    }

    // ファイルの一覧の表示
    void FileSelector::DisplayFileList(bool sortEnable)
    {
        if (sortEnable)
            std::sort((string *)fileNames_,
                      (string *)fileNames_+fileCount_); 
        
        Erase(0, 0, 480, 272);

        div_t m = div(fileCount_, MAX_LIST_PAGE_);
        int count = (m.quot >= page_) ? MAX_LIST_PAGE_ : m.rem;
        for (int n=0; n<count; n++) rect_->Draw(n);
        for (int n=0; n<count; n++)
            fileNameLabels_[n]->Draw(GetFileNameNoExt(n+(page_-1)*MAX_LIST_PAGE_));

        // 前のページ，次のページの選択ボタンなどを表示する
        next_.InactivateAll();
        if (page_ > 1) next_.Activate(0);           // "<" 有効
        if (page_ < maxPage_) next_.Activate(1);    // ">" 有効
        char page[6];
        sprintf(page, "%d/%d", page_, maxPage_);
        pageLabel_.Draw(page);
    }

    // ファイルの一覧の消去
    void FileSelector::Erase(uint16_t x, uint16_t y, uint16_t width,
                             uint16_t height, uint32_t color)
    {
        lcd_.SetTextColor(color);
        lcd_.FillRect(x, y, width, height);
    }

    // 拡張子を削除したファイル名を取得
    string FileSelector::GetFileNameNoExt(int n)
    {
        string name = fileNames_[n];
        int k = name.rfind(".");
        if (k != string::npos)
            return name.erase(k);
        else
            return name;
    }
}

