/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sourceLocation.h"

#include <lexer/token/letters.h>

namespace panda::es2panda::lexer {

void OffsetEntry::AddCol(size_t offset)
{
    size_t diff = offset - offset_;
    offset_ = offset;

    if (ranges.empty()) {
        ranges.emplace_back(Range {diff});
        return;
    }

    auto &range = ranges.back();

    if (diff == range.byteSize) {
        range.cnt++;
    } else {
        ranges.emplace_back(Range {diff});
    }
}

LineIndex::LineIndex(const util::StringView &source) noexcept
{
    auto iter = util::StringView::Iterator(source);
    entrys_.emplace_back(0);

    bool nextEntry = false;
    while (true) {
        if (!iter.HasNext()) {
            if (!nextEntry) {
                // Add the last entry if the ending character is not LEX_CHAR_LF / LEX_CHAR_PS / LEX_CHAR_LS
                entrys_.emplace_back(iter.Index());
            }
            return;
        }

        switch (iter.Next()) {
            case LEX_CHAR_CR: {
                if (iter.HasNext() && iter.Peek() == LEX_CHAR_LF) {
                    iter.Forward(1);
                }

                [[fallthrough]];
            }
            case LEX_CHAR_LF:
            case LEX_CHAR_PS:
            case LEX_CHAR_LS: {
                entrys_.emplace_back(iter.Index());
                nextEntry = true;
                break;
            }
            default: {
                entrys_.back().AddCol(iter.Index());
                nextEntry = false;
            }
        }
    }
}

SourceLocation LineIndex::GetLocation(SourcePosition pos) noexcept
{
    size_t line = pos.line;

    size_t col = 0;
    ASSERT(pos.line < entrys_.size());
    const auto &entry = entrys_[pos.line];
    size_t diff = pos.index - entry.lineStart;

    for (const auto &range : entry.ranges) {
        if (diff < (range.cnt * range.byteSize)) {
            col += (diff / range.byteSize) ;
            break;
        }

        diff -= range.cnt * range.byteSize;
        col += range.cnt;
    }

    return SourceLocation(line + 1, col + 1);
}

}  // namespace panda::es2panda::lexer