/* searchtextstorage.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2008-2015 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "searchtextstorage.hh"
#include "textstorage.h"
#include "textview.h"
#include <cctype>

SearchTextStorage::SearchTextStorage( TextStorage &ts ) :
    m_ts( ts ),
    m_start_search( std::pair<int, int>( -1, -1 ) )
{
    m_cur_match.line = -1;
    m_cur_match.start_offset = -1;
    m_cur_match.end_offset = -1;
}

SearchTextStorage::~SearchTextStorage()
{
}

void SearchTextStorage::search( const std::string &search_string )
{
    if ( search_string.length() < 1 ) {
        m_cur_match.line = -1;
        return;
    }

    std::pair<int, int> start;
    bool ignore_case = true;

    if ( search_string == m_last_search_string && m_cur_match.line >= 0 ) {
        start = std::pair<int, int>( m_cur_match.line, m_cur_match.end_offset );
        start.second++;
    } else {
        start = m_start_search;
    }

    if ( start.first >= m_ts.getNrOfUnwrappedLines() )
        start.first = -1;

    m_cur_match.line = -1;
    
    if ( start.first >= 0 ) {
        std::string l;
        if ( m_ts.getUnwrappedLine( start.first, 0, l ) == 0 ) {
            
            if ( start.second >= (int)l.length() ) {
                start.first++;
                start.second = 0;
            }
        }
    }

    m_cur_match.line = -1;

    if ( start.first < 0 ) {
        start.first = 0;
        start.second = 0;
    }

    for ( std::string::const_iterator it1 = search_string.begin();
          it1 != search_string.end();
          it1++ ) {
        //TODO not utf8 safe
        if ( std::isupper( *it1 ) ) {
            ignore_case = false;
            break;
        }
    }

    if ( start.first >= 0 ) {
        for ( ; start.first < m_ts.getNrOfUnwrappedLines(); start.first++, start.second = 0 ) {
            std::string l;
            if ( m_ts.getUnwrappedLine( start.first, 0, l ) == 0 ) {
                int so = -1, eo = -1;

                if ( start.second >= 0 && start.second < (int)l.length() ) {
                    if ( m_cre.match( search_string.c_str(), l.c_str() + start.second, ignore_case, &so, &eo ) == true ) {
                        m_cur_match.line = start.first;
                        m_cur_match.start_offset = so + start.second;
                        m_cur_match.end_offset = eo + start.second;
                        break;
                    }
                }
            } else {
                break;
            }
        }
        if ( m_cur_match.line == -1 ) {
            // no match so start over next time
            m_start_search.first = -1;
            m_start_search.second = -1;
        }
    }

    m_last_search_string = search_string;
}

void SearchTextStorage::searchBackwards( const std::string &search_string )
{
    if ( search_string.length() < 1 ) {
        m_cur_match.line = -1;
        return;
    }

    std::pair<int, int> start;
    bool ignore_case = true;

    if ( search_string == m_last_search_string && m_cur_match.line >= 0 ) {
        start = std::pair<int, int>( m_cur_match.line, m_cur_match.start_offset );

        prevPos( start );
    } else {
        start = m_start_search;
    }

    if ( start.first >= m_ts.getNrOfUnwrappedLines() ) {
        start.first = -1;
    }

    m_cur_match.line = -1;
    
    m_cur_match.line = -1;

    if ( start.first < 0 ) {
        start.first = 0;
        start.second = 0;

        prevPos( start );
    }

    for ( std::string::const_iterator it1 = search_string.begin();
          it1 != search_string.end();
          it1++ ) {
        //TODO not utf8 safe
        if ( std::isupper( *it1 ) ) {
            ignore_case = false;
            break;
        }
    }

    if ( start.first >= 0 ) {
        for ( ; start.first >= 0; start.first--, start.second = -1 ) {
            std::string l;
            if ( m_ts.getUnwrappedLine( start.first, 0, l ) == 0 ) {
                if ( l.length() < 1 ) continue;

                if ( start.second == -1 ) {
                    start.second = l.length() - 1;
                }

                int match_candidate_so = -1;
                int match_candidate_eo = -1;
                int pos = 0;

                for (;;) {
                    // search match, if lower than start.second, store as candidate
                    // otherwise take possible candidate
                    // repeat from match position + 1
                    if ( pos >= (int)l.length() ) break;

                    int so = -1, eo = -1;

                    if ( m_cre.match( search_string.c_str(), l.c_str() + pos, ignore_case, &so, &eo ) == true ) {

                        if ( so + pos < start.second ) {
                            match_candidate_so = so + pos;
                            match_candidate_eo = eo + pos;

                            pos += so + 1;

                            continue;
                        }
                    }

                    break;
                }

                if ( match_candidate_so >= 0 ) {
                    m_cur_match.line = start.first;
                    m_cur_match.start_offset = match_candidate_so;
                    m_cur_match.end_offset = match_candidate_eo;
                    break;
                }
            } else {
                break;
            }
        }
        if ( m_cur_match.line == -1 ) {
            // no match so start over next time
            m_start_search.first = -1;
            m_start_search.second = -1;
        }
    }

    m_last_search_string = search_string;
}

void SearchTextStorage::highlightCurMatch( TextView &tv )
{
    if ( m_cur_match.line >= 0 ) {
        tv.setSelectionStart( m_cur_match.line, m_cur_match.start_offset );
        tv.setSelectionEnd( m_cur_match.line, m_cur_match.end_offset );
    } else {
        tv.clearSelection();
    }
}

void SearchTextStorage::setSearchStartLine( int line )
{
    if ( line < 0 || line >= m_ts.getNrOfUnwrappedLines() ) return;
    
    m_start_search.first = line;
    m_start_search.second = 0;
    m_cur_match.line = -1;
}

int SearchTextStorage::getCurMatchLine() const
{
    return m_cur_match.line;
}

int SearchTextStorage::prevPos( std::pair< int, int > &pos )
{
    if ( pos.second > 0 ) {
        pos.second--;
        return 0;
    }

    bool wrapped = false;

    if ( pos.first > 0 ) {
        pos.first--;
    } else {
        pos.first = m_ts.getNrOfUnwrappedLines() - 1;
        wrapped = true;
    }

    if ( pos.first >= 0 ) {
        std::string l;
        if ( m_ts.getUnwrappedLine( pos.first, 0, l ) == 0 ) {
            pos.second = l.length();
        } else {
            pos.first = -1;
            pos.second = -1;
            return -1;
        }
    }

    return wrapped == true ? 1 : 0;
}
