/*
 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
 * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc.  All right reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#ifndef BidiResolver_h
#define BidiResolver_h

#include "platform/text/BidiCharacterRun.h"
#include "platform/text/BidiContext.h"
#include "platform/text/BidiRunList.h"
#include "platform/text/TextDirection.h"
#include "platform/wtf/Allocator.h"
#include "platform/wtf/HashMap.h"
#include "platform/wtf/Noncopyable.h"
#include "platform/wtf/Vector.h"

namespace blink {

template <class Iterator>
class MidpointState final {
  DISALLOW_NEW();

 public:
  MidpointState() { Reset(); }

  void Reset() {
    num_midpoints_ = 0;
    current_midpoint_ = 0;
    between_midpoints_ = false;
  }

  void StartIgnoringSpaces(const Iterator& midpoint) {
    DCHECK(!(num_midpoints_ % 2));
    AddMidpoint(midpoint);
  }

  void StopIgnoringSpaces(const Iterator& midpoint) {
    DCHECK(num_midpoints_ % 2);
    AddMidpoint(midpoint);
  }

  // Adding a pair of midpoints before a character will split it out into a new
  // line box.
  void EnsureCharacterGetsLineBox(Iterator& text_paragraph_separator) {
    StartIgnoringSpaces(Iterator(0,
                                 text_paragraph_separator.GetLineLayoutItem(),
                                 text_paragraph_separator.Offset() - 1));
    StopIgnoringSpaces(Iterator(0, text_paragraph_separator.GetLineLayoutItem(),
                                text_paragraph_separator.Offset()));
  }

  void CheckMidpoints(Iterator& l_break) {
    // Check to see if our last midpoint is a start point beyond the line break.
    // If so, shave it off the list, and shave off a trailing space if the
    // previous end point doesn't preserve whitespace.
    if (l_break.GetLineLayoutItem() && num_midpoints_ &&
        !(num_midpoints_ % 2)) {
      Iterator* midpoints_iterator = midpoints_.data();
      Iterator& endpoint = midpoints_iterator[num_midpoints_ - 2];
      const Iterator& startpoint = midpoints_iterator[num_midpoints_ - 1];
      Iterator currpoint = endpoint;
      while (!currpoint.AtEnd() && currpoint != startpoint &&
             currpoint != l_break)
        currpoint.Increment();
      if (currpoint == l_break) {
        // We hit the line break before the start point. Shave off the start
        // point.
        num_midpoints_--;
        if (endpoint.GetLineLayoutItem().Style()->CollapseWhiteSpace() &&
            endpoint.GetLineLayoutItem().IsText())
          endpoint.SetOffset(endpoint.Offset() - 1);
      }
    }
  }

  Vector<Iterator>& Midpoints() { return midpoints_; }
  const unsigned& NumMidpoints() const { return num_midpoints_; }
  const unsigned& CurrentMidpoint() const { return current_midpoint_; }
  void IncrementCurrentMidpoint() { current_midpoint_++; }
  const bool& BetweenMidpoints() const { return between_midpoints_; }
  void SetBetweenMidpoints(bool between_midpoint) {
    between_midpoints_ = between_midpoint;
  }

 private:
  // The goal is to reuse the line state across multiple
  // lines so we just keep an array around for midpoints and never clear it
  // across multiple lines. We track the number of items and position using the
  // two other variables.
  Vector<Iterator> midpoints_;
  unsigned num_midpoints_;
  unsigned current_midpoint_;
  bool between_midpoints_;

  void AddMidpoint(const Iterator& midpoint) {
    if (midpoints_.size() <= num_midpoints_)
      midpoints_.Grow(num_midpoints_ + 10);

    Iterator* midpoints_iterator = midpoints_.data();
    midpoints_iterator[num_midpoints_++] = midpoint;
  }
};

// The BidiStatus at a given position (typically the end of a line) can
// be cached and then used to restart bidi resolution at that position.
struct BidiStatus final {
  DISALLOW_NEW();
  BidiStatus()
      : eor(WTF::Unicode::kOtherNeutral),
        last_strong(WTF::Unicode::kOtherNeutral),
        last(WTF::Unicode::kOtherNeutral) {}

  // Creates a BidiStatus representing a new paragraph root with a default
  // direction.  Uses TextDirection as it only has two possibilities instead of
  // WTF::Unicode::Direction which has 19.
  BidiStatus(TextDirection text_direction, bool is_override) {
    WTF::Unicode::CharDirection direction =
        text_direction == TextDirection::kLtr ? WTF::Unicode::kLeftToRight
                                              : WTF::Unicode::kRightToLeft;
    eor = last_strong = last = direction;
    context = BidiContext::Create(text_direction == TextDirection::kLtr ? 0 : 1,
                                  direction, is_override);
  }

  BidiStatus(WTF::Unicode::CharDirection eor_dir,
             WTF::Unicode::CharDirection last_strong_dir,
             WTF::Unicode::CharDirection last_dir,
             RefPtr<BidiContext> bidi_context)
      : eor(eor_dir),
        last_strong(last_strong_dir),
        last(last_dir),
        context(std::move(bidi_context)) {}

  // Creates a BidiStatus for Isolates (RLI/LRI).
  // The rule X5a ans X5b of UAX#9: http://unicode.org/reports/tr9/#X5a
  static BidiStatus CreateForIsolate(TextDirection text_direction,
                                     bool is_override,
                                     unsigned char level) {
    WTF::Unicode::CharDirection direction;
    if (text_direction == TextDirection::kRtl) {
      level = NextGreaterOddLevel(level);
      direction = WTF::Unicode::kRightToLeft;
    } else {
      level = NextGreaterEvenLevel(level);
      direction = WTF::Unicode::kLeftToRight;
    }
    RefPtr<BidiContext> context =
        BidiContext::Create(level, direction, is_override, kFromStyleOrDOM);

    // This copies BidiStatus and may churn the ref on BidiContext.
    // I doubt it matters.
    return BidiStatus(direction, direction, direction, std::move(context));
  }

  WTF::Unicode::CharDirection eor;
  WTF::Unicode::CharDirection last_strong;
  WTF::Unicode::CharDirection last;
  RefPtr<BidiContext> context;
};

class BidiEmbedding final {
  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();

 public:
  BidiEmbedding(WTF::Unicode::CharDirection direction,
                BidiEmbeddingSource source)
      : direction_(direction), source_(source) {}

  WTF::Unicode::CharDirection Direction() const { return direction_; }
  BidiEmbeddingSource Source() const { return source_; }

 private:
  WTF::Unicode::CharDirection direction_;
  BidiEmbeddingSource source_;
};

inline bool operator==(const BidiStatus& status1, const BidiStatus& status2) {
  return status1.eor == status2.eor && status1.last == status2.last &&
         status1.last_strong == status2.last_strong &&
         *(status1.context) == *(status2.context);
}

inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2) {
  return !(status1 == status2);
}

enum VisualDirectionOverride {
  kNoVisualOverride,
  kVisualLeftToRightOverride,
  kVisualRightToLeftOverride
};

class NoIsolatedRun {};

// BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
// http://unicode.org/reports/tr9
template <class Iterator, class Run, class IsolatedRun = NoIsolatedRun>
class BidiResolver final {
  DISALLOW_NEW();
  WTF_MAKE_NONCOPYABLE(BidiResolver);

 public:
  BidiResolver()
      : direction_(WTF::Unicode::kOtherNeutral),
        reached_end_of_line_(false),
        empty_run_(true),
        nested_isolate_count_(0),
        trailing_space_run_(0),
        needs_trailing_space_(false) {}

#if DCHECK_IS_ON()
  ~BidiResolver();
#endif

  const Iterator& GetPosition() const { return current_; }
  Iterator& GetPosition() { return current_; }
  void SetPositionIgnoringNestedIsolates(const Iterator& position) {
    current_ = position;
  }
  void SetPosition(const Iterator& position, unsigned nested_isolated_count) {
    current_ = position;
    nested_isolate_count_ = nested_isolated_count;
  }

  BidiContext* Context() const { return status_.context.Get(); }
  void SetContext(RefPtr<BidiContext> c) { status_.context = std::move(c); }

  void SetLastDir(WTF::Unicode::CharDirection last_dir) {
    status_.last = last_dir;
  }
  void SetLastStrongDir(WTF::Unicode::CharDirection last_strong_dir) {
    status_.last_strong = last_strong_dir;
  }
  void SetEorDir(WTF::Unicode::CharDirection eor_dir) { status_.eor = eor_dir; }

  WTF::Unicode::CharDirection Dir() const { return direction_; }
  void SetDir(WTF::Unicode::CharDirection d) { direction_ = d; }

  const BidiStatus& Status() const { return status_; }
  void SetStatus(const BidiStatus s) {
    DCHECK(s.context);
    status_ = s;
    paragraph_directionality_ = s.context->Dir() == WTF::Unicode::kLeftToRight
                                    ? TextDirection::kLtr
                                    : TextDirection::kRtl;
  }

  MidpointState<Iterator>& GetMidpointState() { return midpoint_state_; }

  // The current algorithm handles nested isolates one layer of nesting at a
  // time.  But when we layout each isolated span, we will walk into (and
  // ignore) all child isolated spans.
  void EnterIsolate() { nested_isolate_count_++; }
  void ExitIsolate() {
    DCHECK_GE(nested_isolate_count_, 1u);
    nested_isolate_count_--;
  }
  bool InIsolate() const { return nested_isolate_count_; }

  void Embed(WTF::Unicode::CharDirection, BidiEmbeddingSource);
  bool CommitExplicitEmbedding(BidiRunList<Run>&);

  void CreateBidiRunsForLine(const Iterator& end,
                             VisualDirectionOverride = kNoVisualOverride,
                             bool hard_line_break = false,
                             bool reorder_runs = true);

  BidiRunList<Run>& Runs() { return runs_; }

  // FIXME: This used to be part of deleteRuns() but was a layering violation.
  // It's unclear if this is still needed.
  void MarkCurrentRunEmpty() { empty_run_ = true; }

  Vector<IsolatedRun>& IsolatedRuns() { return isolated_runs_; }

  bool IsEndOfLine(const Iterator& end) {
    return current_ == end || current_.AtEnd();
  }

  TextDirection DetermineParagraphDirectionality(
      bool* has_strong_directionality = 0) {
    bool break_on_paragraph = true;
    return DetermineDirectionalityInternal(break_on_paragraph,
                                           has_strong_directionality);
  }
  TextDirection DetermineDirectionality(bool* has_strong_directionality = 0) {
    bool break_on_paragraph = false;
    return DetermineDirectionalityInternal(break_on_paragraph,
                                           has_strong_directionality);
  }

  void SetMidpointStateForIsolatedRun(Run&, const MidpointState<Iterator>&);
  MidpointState<Iterator> MidpointStateForIsolatedRun(Run&);

  Iterator EndOfLine() const { return end_of_line_; }

  void SetNeedsTrailingSpace(bool value) { needs_trailing_space_ = value; }
  Run* TrailingSpaceRun() const { return trailing_space_run_; }

 protected:
  void Increment() { current_.Increment(); }
  // FIXME: Instead of InlineBidiResolvers subclassing this method, we should
  // pass in some sort of Traits object which knows how to create runs for
  // appending.
  void AppendRun(BidiRunList<Run>&);

  Run* AddTrailingRun(BidiRunList<Run>&,
                      int,
                      int,
                      Run*,
                      BidiContext*,
                      TextDirection) const {
    return 0;
  }
  Iterator current_;
  // sor and eor are "start of run" and "end of run" respectively and correpond
  // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7
  Iterator sor_;  // Points to the first character in the current run.
  Iterator eor_;  // Points to the last character in the current run.
  Iterator last_;
  BidiStatus status_;
  WTF::Unicode::CharDirection direction_;
  // m_endOfRunAtEndOfLine is "the position last eor in the end of line"
  Iterator end_of_run_at_end_of_line_;
  Iterator end_of_line_;
  bool reached_end_of_line_;
  Iterator last_before_et_;  // Before a EuropeanNumberTerminator
  bool empty_run_;

  // FIXME: This should not belong to the resolver, but rather be passed
  // into createBidiRunsForLine by the caller.
  BidiRunList<Run> runs_;

  MidpointState<Iterator> midpoint_state_;

  unsigned nested_isolate_count_;
  Vector<IsolatedRun> isolated_runs_;
  Run* trailing_space_run_;
  bool needs_trailing_space_;
  TextDirection paragraph_directionality_;

 private:
  void RaiseExplicitEmbeddingLevel(BidiRunList<Run>&,
                                   WTF::Unicode::CharDirection from,
                                   WTF::Unicode::CharDirection to);
  void LowerExplicitEmbeddingLevel(BidiRunList<Run>&,
                                   WTF::Unicode::CharDirection from);
  void CheckDirectionInLowerRaiseEmbeddingLevel();

  void UpdateStatusLastFromCurrentDirection(WTF::Unicode::CharDirection);
  void ReorderRunsFromLevels(BidiRunList<Run>&) const;

  bool NeedsTrailingSpace(BidiRunList<Run>&) { return needs_trailing_space_; }
  int FindFirstTrailingSpaceAtRun(Run*) { return 0; }
  // http://www.unicode.org/reports/tr9/#L1
  void ComputeTrailingSpace(BidiRunList<Run>&);

  TextDirection DetermineDirectionalityInternal(
      bool break_on_paragraph,
      bool* has_strong_directionality);

  Vector<BidiEmbedding, 8> current_explicit_embedding_sequence_;
  HashMap<Run*, MidpointState<Iterator>> midpoint_state_for_isolated_run_;
};

#if DCHECK_IS_ON()
template <class Iterator, class Run, class IsolatedRun>
BidiResolver<Iterator, Run, IsolatedRun>::~BidiResolver() {
  // The owner of this resolver should have handled the isolated runs.
  DCHECK(isolated_runs_.IsEmpty());
  DCHECK(!runs_.RunCount());
}
#endif

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::AppendRun(
    BidiRunList<Run>& runs) {
  if (!empty_run_ && !eor_.AtEnd()) {
    unsigned start_offset = sor_.Offset();
    unsigned end_offset = eor_.Offset();

    if (!end_of_run_at_end_of_line_.AtEnd() &&
        end_offset >= end_of_run_at_end_of_line_.Offset()) {
      reached_end_of_line_ = true;
      end_offset = end_of_run_at_end_of_line_.Offset();
    }

    // m_eor and m_endOfRunAtEndOfLine are inclusive while BidiRun's stop is
    // exclusive so offset needs to be increased by one.
    end_offset += 1;

    // Append BidiRun objects, at most 64K chars at a time, until all
    // text between |startOffset| and |endOffset| is represented.
    while (start_offset < end_offset) {
      unsigned end = end_offset;
      const int kLimit =
          USHRT_MAX;  // InlineTextBox stores text length as unsigned short.
      if (end - start_offset > kLimit)
        end = start_offset + kLimit;
      runs.AddRun(new Run(Context()->Override(), Context()->Level(),
                          start_offset, end, direction_, Context()->Dir()));
      start_offset = end;
    }

    eor_.Increment();
    sor_ = eor_;
  }

  direction_ = WTF::Unicode::kOtherNeutral;
  status_.eor = WTF::Unicode::kOtherNeutral;
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::Embed(
    WTF::Unicode::CharDirection dir,
    BidiEmbeddingSource source) {
  // Isolated spans compute base directionality during their own UBA run.
  // Do not insert fake embed characters once we enter an isolated span.
  DCHECK(!InIsolate());
  using namespace WTF::Unicode;

  DCHECK(dir == kPopDirectionalFormat || dir == kLeftToRightEmbedding ||
         dir == kLeftToRightOverride || dir == kRightToLeftEmbedding ||
         dir == kRightToLeftOverride);
  current_explicit_embedding_sequence_.push_back(BidiEmbedding(dir, source));
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::
    CheckDirectionInLowerRaiseEmbeddingLevel() {
  using namespace WTF::Unicode;

  DCHECK(status_.eor != kOtherNeutral || eor_.AtEnd());
  DCHECK_NE(status_.last, kNonSpacingMark);
  DCHECK_NE(status_.last, kBoundaryNeutral);
  DCHECK_NE(status_.last, kRightToLeftEmbedding);
  DCHECK_NE(status_.last, kLeftToRightEmbedding);
  DCHECK_NE(status_.last, kRightToLeftOverride);
  DCHECK_NE(status_.last, kLeftToRightOverride);
  DCHECK_NE(status_.last, kPopDirectionalFormat);
  if (direction_ == kOtherNeutral)
    direction_ =
        status_.last_strong == kLeftToRight ? kLeftToRight : kRightToLeft;
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::LowerExplicitEmbeddingLevel(
    BidiRunList<Run>& runs,
    WTF::Unicode::CharDirection from) {
  using namespace WTF::Unicode;

  if (!empty_run_ && eor_ != last_) {
    CheckDirectionInLowerRaiseEmbeddingLevel();
    // bidi.sor ... bidi.eor ... bidi.last eor; need to append the
    // bidi.sor-bidi.eor run or extend it through bidi.last
    if (from == kLeftToRight) {
      // bidi.sor ... bidi.eor ... bidi.last L
      if (status_.eor == kEuropeanNumber) {
        if (status_.last_strong != kLeftToRight) {
          direction_ = kEuropeanNumber;
          AppendRun(runs);
        }
      } else if (status_.eor == kArabicNumber) {
        direction_ = kArabicNumber;
        AppendRun(runs);
      } else if (status_.last_strong != kLeftToRight) {
        AppendRun(runs);
        direction_ = kLeftToRight;
      }
    } else if (status_.eor == kEuropeanNumber || status_.eor == kArabicNumber ||
               status_.last_strong == kLeftToRight) {
      AppendRun(runs);
      direction_ = kRightToLeft;
    }
    eor_ = last_;
  }

  AppendRun(runs);
  empty_run_ = true;

  // sor for the new run is determined by the higher level (rule X10)
  SetLastDir(from);
  SetLastStrongDir(from);
  eor_ = Iterator();
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::RaiseExplicitEmbeddingLevel(
    BidiRunList<Run>& runs,
    WTF::Unicode::CharDirection from,
    WTF::Unicode::CharDirection to) {
  using namespace WTF::Unicode;

  if (!empty_run_ && eor_ != last_) {
    CheckDirectionInLowerRaiseEmbeddingLevel();
    // bidi.sor ... bidi.eor ... bidi.last eor; need to append the
    // bidi.sor-bidi.eor run or extend it through bidi.last
    if (to == kLeftToRight) {
      // bidi.sor ... bidi.eor ... bidi.last L
      if (status_.eor == kEuropeanNumber) {
        if (status_.last_strong != kLeftToRight) {
          direction_ = kEuropeanNumber;
          AppendRun(runs);
        }
      } else if (status_.eor == kArabicNumber) {
        direction_ = kArabicNumber;
        AppendRun(runs);
      } else if (status_.last_strong != kLeftToRight && from == kLeftToRight) {
        AppendRun(runs);
        direction_ = kLeftToRight;
      }
    } else if (status_.eor == kArabicNumber ||
               (status_.eor == kEuropeanNumber &&
                (status_.last_strong != kLeftToRight ||
                 from == kRightToLeft)) ||
               (status_.eor != kEuropeanNumber &&
                status_.last_strong == kLeftToRight && from == kRightToLeft)) {
      AppendRun(runs);
      direction_ = kRightToLeft;
    }
    eor_ = last_;
  }

  AppendRun(runs);
  empty_run_ = true;

  SetLastDir(to);
  SetLastStrongDir(to);
  eor_ = Iterator();
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::ComputeTrailingSpace(
    BidiRunList<Run>& runs) {
  DCHECK(runs.RunCount());

  Run* trailing_space_run = runs.LogicallyLastRun();

  int first_space = FindFirstTrailingSpaceAtRun(trailing_space_run);
  if (first_space == trailing_space_run->Stop())
    return;

  bool should_reorder =
      trailing_space_run != (paragraph_directionality_ == TextDirection::kLtr
                                 ? runs.LastRun()
                                 : runs.FirstRun());
  if (first_space != trailing_space_run->Start()) {
    BidiContext* base_context = Context();
    while (BidiContext* parent = base_context->Parent())
      base_context = parent;

    trailing_space_run_ = AddTrailingRun(
        runs, first_space, trailing_space_run->stop_, trailing_space_run,
        base_context, paragraph_directionality_);
    DCHECK(trailing_space_run_);
    trailing_space_run->stop_ = first_space;
    return;
  }
  if (!should_reorder) {
    trailing_space_run_ = trailing_space_run;
    return;
  }

  // Apply L1 rule.
  if (paragraph_directionality_ == TextDirection::kLtr) {
    runs.MoveRunToEnd(trailing_space_run);
    trailing_space_run->level_ = 0;
  } else {
    runs.MoveRunToBeginning(trailing_space_run);
    trailing_space_run->level_ = 1;
  }
  trailing_space_run_ = trailing_space_run;
}

template <class Iterator, class Run, class IsolatedRun>
bool BidiResolver<Iterator, Run, IsolatedRun>::CommitExplicitEmbedding(
    BidiRunList<Run>& runs) {
  // When we're "inIsolate()" we're resolving the parent context which
  // ignores (skips over) the isolated content, including embedding levels.
  // We should never accrue embedding levels while skipping over isolated
  // content.
  DCHECK(!InIsolate() || current_explicit_embedding_sequence_.IsEmpty());

  using namespace WTF::Unicode;

  unsigned char from_level = Context()->Level();
  RefPtr<BidiContext> to_context = Context();

  for (size_t i = 0; i < current_explicit_embedding_sequence_.size(); ++i) {
    BidiEmbedding embedding = current_explicit_embedding_sequence_[i];
    if (embedding.Direction() == kPopDirectionalFormat) {
      if (BidiContext* parent_context = to_context->Parent())
        to_context = parent_context;
    } else {
      CharDirection direction =
          (embedding.Direction() == kRightToLeftEmbedding ||
           embedding.Direction() == kRightToLeftOverride)
              ? kRightToLeft
              : kLeftToRight;
      bool override = embedding.Direction() == kLeftToRightOverride ||
                      embedding.Direction() == kRightToLeftOverride;
      unsigned char level = to_context->Level();
      if (direction == kRightToLeft)
        level = NextGreaterOddLevel(level);
      else
        level = NextGreaterEvenLevel(level);
      if (level < BidiContext::kMaxLevel)
        to_context = BidiContext::Create(level, direction, override,
                                         embedding.Source(), to_context.Get());
    }
  }

  unsigned char to_level = to_context->Level();

  if (to_level > from_level)
    RaiseExplicitEmbeddingLevel(runs,
                                from_level % 2 ? kRightToLeft : kLeftToRight,
                                to_level % 2 ? kRightToLeft : kLeftToRight);
  else if (to_level < from_level)
    LowerExplicitEmbeddingLevel(runs,
                                from_level % 2 ? kRightToLeft : kLeftToRight);

  SetContext(to_context);

  current_explicit_embedding_sequence_.clear();

  return from_level != to_level;
}

template <class Iterator, class Run, class IsolatedRun>
inline void
BidiResolver<Iterator, Run, IsolatedRun>::UpdateStatusLastFromCurrentDirection(
    WTF::Unicode::CharDirection dir_current) {
  using namespace WTF::Unicode;
  switch (dir_current) {
    case kEuropeanNumberTerminator:
      if (status_.last != kEuropeanNumber)
        status_.last = kEuropeanNumberTerminator;
      break;
    case kEuropeanNumberSeparator:
    case kCommonNumberSeparator:
    case kSegmentSeparator:
    case kWhiteSpaceNeutral:
    case kOtherNeutral:
      switch (status_.last) {
        case kLeftToRight:
        case kRightToLeft:
        case kRightToLeftArabic:
        case kEuropeanNumber:
        case kArabicNumber:
          status_.last = dir_current;
          break;
        default:
          status_.last = kOtherNeutral;
      }
      break;
    case kNonSpacingMark:
    case kBoundaryNeutral:
    case kRightToLeftEmbedding:
    case kLeftToRightEmbedding:
    case kRightToLeftOverride:
    case kLeftToRightOverride:
    case kPopDirectionalFormat:
      // ignore these
      break;
    case kEuropeanNumber:
    // fall through
    default:
      status_.last = dir_current;
  }
}

template <class Iterator, class Run, class IsolatedRun>
inline void BidiResolver<Iterator, Run, IsolatedRun>::ReorderRunsFromLevels(
    BidiRunList<Run>& runs) const {
  unsigned char level_low = BidiContext::kMaxLevel;
  unsigned char level_high = 0;
  for (Run* run = runs.FirstRun(); run; run = run->Next()) {
    level_high = std::max(run->Level(), level_high);
    level_low = std::min(run->Level(), level_low);
  }

  // This implements reordering of the line (L2 according to Bidi spec):
  // http://unicode.org/reports/tr9/#L2
  // L2. From the highest level found in the text to the lowest odd level on
  // each line, reverse any contiguous sequence of characters that are at that
  // level or higher.

  // Reversing is only done up to the lowest odd level.
  if (!(level_low % 2))
    level_low++;

  unsigned count = runs.RunCount() - 1;

  while (level_high >= level_low) {
    unsigned i = 0;
    Run* run = runs.FirstRun();
    while (i < count) {
      for (; i < count && run && run->Level() < level_high; i++)
        run = run->Next();
      unsigned start = i;
      for (; i <= count && run && run->Level() >= level_high; i++)
        run = run->Next();
      unsigned end = i - 1;
      runs.ReverseRuns(start, end);
    }
    level_high--;
  }
}

template <class Iterator, class Run, class IsolatedRun>
TextDirection
BidiResolver<Iterator, Run, IsolatedRun>::DetermineDirectionalityInternal(
    bool break_on_paragraph,
    bool* has_strong_directionality) {
  while (!current_.AtEnd()) {
    if (InIsolate()) {
      Increment();
      continue;
    }
    if (break_on_paragraph && current_.AtParagraphSeparator())
      break;
    UChar32 current = current_.Current();
    if (UNLIKELY(U16_IS_SURROGATE(current))) {
      Increment();
      // If this not the high part of the surrogate pair, then drop it and move
      // to the next.
      if (!U16_IS_SURROGATE_LEAD(current))
        continue;
      UChar high = static_cast<UChar>(current);
      if (current_.AtEnd())
        continue;
      UChar low = current_.Current();
      // Verify the low part. If invalid, then assume an invalid surrogate pair
      // and retry.
      if (!U16_IS_TRAIL(low))
        continue;
      current = U16_GET_SUPPLEMENTARY(high, low);
    }
    WTF::Unicode::CharDirection char_direction =
        WTF::Unicode::Direction(current);
    if (char_direction == WTF::Unicode::kLeftToRight) {
      if (has_strong_directionality)
        *has_strong_directionality = true;
      return TextDirection::kLtr;
    }
    if (char_direction == WTF::Unicode::kRightToLeft ||
        char_direction == WTF::Unicode::kRightToLeftArabic) {
      if (has_strong_directionality)
        *has_strong_directionality = true;
      return TextDirection::kRtl;
    }
    Increment();
  }
  if (has_strong_directionality)
    *has_strong_directionality = false;
  return TextDirection::kLtr;
}

inline TextDirection DirectionForCharacter(UChar32 character) {
  WTF::Unicode::CharDirection char_direction =
      WTF::Unicode::Direction(character);
  if (char_direction == WTF::Unicode::kRightToLeft ||
      char_direction == WTF::Unicode::kRightToLeftArabic)
    return TextDirection::kRtl;
  return TextDirection::kLtr;
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::CreateBidiRunsForLine(
    const Iterator& end,
    VisualDirectionOverride override,
    bool hard_line_break,
    bool reorder_runs) {
  using namespace WTF::Unicode;

  DCHECK_EQ(direction_, kOtherNeutral);
  trailing_space_run_ = 0;

  end_of_line_ = end;

  if (override != kNoVisualOverride) {
    empty_run_ = false;
    sor_ = current_;
    eor_ = Iterator();
    while (current_ != end && !current_.AtEnd()) {
      eor_ = current_;
      Increment();
    }
    direction_ =
        override == kVisualLeftToRightOverride ? kLeftToRight : kRightToLeft;
    AppendRun(runs_);
    runs_.SetLogicallyLastRun(runs_.LastRun());
    if (override == kVisualRightToLeftOverride && runs_.RunCount())
      runs_.ReverseRuns(0, runs_.RunCount() - 1);
    return;
  }

  empty_run_ = true;

  eor_ = Iterator();

  last_ = current_;
  bool last_line_ended = false;
  BidiResolver<Iterator, Run, IsolatedRun> state_at_end;

  while (true) {
    if (InIsolate() && empty_run_) {
      sor_ = current_;
      empty_run_ = false;
    }

    if (!last_line_ended && IsEndOfLine(end)) {
      if (empty_run_)
        break;

      state_at_end.status_ = status_;
      state_at_end.sor_ = sor_;
      state_at_end.eor_ = eor_;
      state_at_end.last_ = last_;
      state_at_end.reached_end_of_line_ = reached_end_of_line_;
      state_at_end.last_before_et_ = last_before_et_;
      state_at_end.empty_run_ = empty_run_;
      end_of_run_at_end_of_line_ = last_;
      last_line_ended = true;
    }
    CharDirection dir_current;
    if (last_line_ended && (hard_line_break || current_.AtEnd())) {
      BidiContext* c = Context();
      if (hard_line_break) {
        // A deviation from the Unicode Bidi Algorithm in order to match
        // WinIE and user expectations: hard line breaks reset bidi state
        // coming from unicode bidi control characters, but not those from
        // DOM nodes with specified directionality
        state_at_end.SetContext(c->CopyStackRemovingUnicodeEmbeddingContexts());

        dir_current = state_at_end.Context()->Dir();
        state_at_end.SetEorDir(dir_current);
        state_at_end.SetLastDir(dir_current);
        state_at_end.SetLastStrongDir(dir_current);
      } else {
        while (c->Parent())
          c = c->Parent();
        dir_current = c->Dir();
      }
    } else {
      dir_current = current_.Direction();
      if (Context()->Override() && dir_current != kRightToLeftEmbedding &&
          dir_current != kLeftToRightEmbedding &&
          dir_current != kRightToLeftOverride &&
          dir_current != kLeftToRightOverride &&
          dir_current != kPopDirectionalFormat)
        dir_current = Context()->Dir();
      else if (dir_current == kNonSpacingMark)
        dir_current = status_.last;
    }

    // We ignore all character directionality while in unicode-bidi: isolate
    // spans.  We'll handle ordering the isolated characters in a second pass.
    if (InIsolate())
      dir_current = kOtherNeutral;

    DCHECK(status_.eor != kOtherNeutral || eor_.AtEnd());
    switch (dir_current) {
      // embedding and overrides (X1-X9 in the Bidi specs)
      case kRightToLeftEmbedding:
      case kLeftToRightEmbedding:
      case kRightToLeftOverride:
      case kLeftToRightOverride:
      case kPopDirectionalFormat:
        Embed(dir_current, kFromUnicode);
        CommitExplicitEmbedding(runs_);
        break;

      // strong types
      case kLeftToRight:
        switch (status_.last) {
          case kRightToLeft:
          case kRightToLeftArabic:
          case kEuropeanNumber:
          case kArabicNumber:
            if (status_.last != kEuropeanNumber ||
                status_.last_strong != kLeftToRight)
              AppendRun(runs_);
            break;
          case kLeftToRight:
            break;
          case kEuropeanNumberSeparator:
          case kEuropeanNumberTerminator:
          case kCommonNumberSeparator:
          case kBoundaryNeutral:
          case kBlockSeparator:
          case kSegmentSeparator:
          case kWhiteSpaceNeutral:
          case kOtherNeutral:
            if (status_.eor == kEuropeanNumber) {
              if (status_.last_strong != kLeftToRight) {
                // the numbers need to be on a higher embedding level, so let's
                // close that run
                direction_ = kEuropeanNumber;
                AppendRun(runs_);
                if (Context()->Dir() != kLeftToRight) {
                  // the neutrals take the embedding direction, which is R
                  eor_ = last_;
                  direction_ = kRightToLeft;
                  AppendRun(runs_);
                }
              }
            } else if (status_.eor == kArabicNumber) {
              // Arabic numbers are always on a higher embedding level, so let's
              // close that run
              direction_ = kArabicNumber;
              AppendRun(runs_);
              if (Context()->Dir() != kLeftToRight) {
                // the neutrals take the embedding direction, which is R
                eor_ = last_;
                direction_ = kRightToLeft;
                AppendRun(runs_);
              }
            } else if (status_.last_strong != kLeftToRight) {
              // last stuff takes embedding dir
              if (Context()->Dir() == kRightToLeft) {
                eor_ = last_;
                direction_ = kRightToLeft;
              }
              AppendRun(runs_);
            }
          default:
            break;
        }
        eor_ = current_;
        status_.eor = kLeftToRight;
        status_.last_strong = kLeftToRight;
        direction_ = kLeftToRight;
        break;
      case kRightToLeftArabic:
      case kRightToLeft:
        switch (status_.last) {
          case kLeftToRight:
          case kEuropeanNumber:
          case kArabicNumber:
            AppendRun(runs_);
          case kRightToLeft:
          case kRightToLeftArabic:
            break;
          case kEuropeanNumberSeparator:
          case kEuropeanNumberTerminator:
          case kCommonNumberSeparator:
          case kBoundaryNeutral:
          case kBlockSeparator:
          case kSegmentSeparator:
          case kWhiteSpaceNeutral:
          case kOtherNeutral:
            if (status_.eor == kEuropeanNumber) {
              if (status_.last_strong == kLeftToRight &&
                  Context()->Dir() == kLeftToRight)
                eor_ = last_;
              AppendRun(runs_);
            } else if (status_.eor == kArabicNumber) {
              AppendRun(runs_);
            } else if (status_.last_strong == kLeftToRight) {
              if (Context()->Dir() == kLeftToRight)
                eor_ = last_;
              AppendRun(runs_);
            }
          default:
            break;
        }
        eor_ = current_;
        status_.eor = kRightToLeft;
        status_.last_strong = dir_current;
        direction_ = kRightToLeft;
        break;

      // weak types:

      case kEuropeanNumber:
        if (status_.last_strong != kRightToLeftArabic) {
          // if last strong was AL change EN to AN
          switch (status_.last) {
            case kEuropeanNumber:
            case kLeftToRight:
              break;
            case kRightToLeft:
            case kRightToLeftArabic:
            case kArabicNumber:
              eor_ = last_;
              AppendRun(runs_);
              direction_ = kEuropeanNumber;
              break;
            case kEuropeanNumberSeparator:
            case kCommonNumberSeparator:
              if (status_.eor == kEuropeanNumber)
                break;
            case kEuropeanNumberTerminator:
            case kBoundaryNeutral:
            case kBlockSeparator:
            case kSegmentSeparator:
            case kWhiteSpaceNeutral:
            case kOtherNeutral:
              if (status_.eor == kEuropeanNumber) {
                if (status_.last_strong == kRightToLeft) {
                  // ENs on both sides behave like Rs, so the neutrals should be
                  // R.  Terminate the EN run.
                  AppendRun(runs_);
                  // Make an R run.
                  eor_ = status_.last == kEuropeanNumberTerminator
                             ? last_before_et_
                             : last_;
                  direction_ = kRightToLeft;
                  AppendRun(runs_);
                  // Begin a new EN run.
                  direction_ = kEuropeanNumber;
                }
              } else if (status_.eor == kArabicNumber) {
                // Terminate the AN run.
                AppendRun(runs_);
                if (status_.last_strong == kRightToLeft ||
                    Context()->Dir() == kRightToLeft) {
                  // Make an R run.
                  eor_ = status_.last == kEuropeanNumberTerminator
                             ? last_before_et_
                             : last_;
                  direction_ = kRightToLeft;
                  AppendRun(runs_);
                  // Begin a new EN run.
                  direction_ = kEuropeanNumber;
                }
              } else if (status_.last_strong == kRightToLeft) {
                // Extend the R run to include the neutrals.
                eor_ = status_.last == kEuropeanNumberTerminator
                           ? last_before_et_
                           : last_;
                direction_ = kRightToLeft;
                AppendRun(runs_);
                // Begin a new EN run.
                direction_ = kEuropeanNumber;
              }
            default:
              break;
          }
          eor_ = current_;
          status_.eor = kEuropeanNumber;
          if (direction_ == kOtherNeutral)
            direction_ = kLeftToRight;
          break;
        }
      case kArabicNumber:
        dir_current = kArabicNumber;
        switch (status_.last) {
          case kLeftToRight:
            if (Context()->Dir() == kLeftToRight)
              AppendRun(runs_);
            break;
          case kArabicNumber:
            break;
          case kRightToLeft:
          case kRightToLeftArabic:
          case kEuropeanNumber:
            eor_ = last_;
            AppendRun(runs_);
            break;
          case kCommonNumberSeparator:
            if (status_.eor == kArabicNumber)
              break;
          case kEuropeanNumberSeparator:
          case kEuropeanNumberTerminator:
          case kBoundaryNeutral:
          case kBlockSeparator:
          case kSegmentSeparator:
          case kWhiteSpaceNeutral:
          case kOtherNeutral:
            if (status_.eor == kArabicNumber ||
                (status_.eor == kEuropeanNumber &&
                 (status_.last_strong == kRightToLeft ||
                  Context()->Dir() == kRightToLeft)) ||
                (status_.eor != kEuropeanNumber &&
                 status_.last_strong == kLeftToRight &&
                 Context()->Dir() == kRightToLeft)) {
              // Terminate the run before the neutrals.
              AppendRun(runs_);
              // Begin an R run for the neutrals.
              direction_ = kRightToLeft;
            } else if (direction_ == kOtherNeutral) {
              direction_ = status_.last_strong == kLeftToRight ? kLeftToRight
                                                               : kRightToLeft;
            }
            eor_ = last_;
            AppendRun(runs_);
          default:
            break;
        }
        eor_ = current_;
        status_.eor = kArabicNumber;
        if (direction_ == kOtherNeutral)
          direction_ = kArabicNumber;
        break;
      case kEuropeanNumberSeparator:
      case kCommonNumberSeparator:
        break;
      case kEuropeanNumberTerminator:
        if (status_.last == kEuropeanNumber) {
          dir_current = kEuropeanNumber;
          eor_ = current_;
          status_.eor = dir_current;
        } else if (status_.last != kEuropeanNumberTerminator) {
          last_before_et_ = empty_run_ ? eor_ : last_;
        }
        break;

      // boundary neutrals should be ignored
      case kBoundaryNeutral:
        if (eor_ == last_)
          eor_ = current_;
        break;
      // neutrals
      case kBlockSeparator:
        // ### what do we do with newline and paragraph seperators that come to
        // here?
        break;
      case kSegmentSeparator:
        // ### implement rule L1
        break;
      case kWhiteSpaceNeutral:
        break;
      case kOtherNeutral:
        break;
      default:
        break;
    }

    if (last_line_ended && eor_ == current_) {
      if (!reached_end_of_line_) {
        eor_ = end_of_run_at_end_of_line_;
        switch (status_.eor) {
          case kLeftToRight:
          case kRightToLeft:
          case kArabicNumber:
            direction_ = status_.eor;
            break;
          case kEuropeanNumber:
            direction_ = status_.last_strong == kLeftToRight ? kLeftToRight
                                                             : kEuropeanNumber;
            break;
          default:
            NOTREACHED();
        }
        AppendRun(runs_);
      }
      current_ = end;
      status_ = state_at_end.status_;
      sor_ = state_at_end.sor_;
      eor_ = state_at_end.eor_;
      last_ = state_at_end.last_;
      reached_end_of_line_ = state_at_end.reached_end_of_line_;
      last_before_et_ = state_at_end.last_before_et_;
      empty_run_ = state_at_end.empty_run_;
      direction_ = kOtherNeutral;
      break;
    }

    UpdateStatusLastFromCurrentDirection(dir_current);
    last_ = current_;

    if (empty_run_) {
      sor_ = current_;
      empty_run_ = false;
    }

    Increment();
    if (!current_explicit_embedding_sequence_.IsEmpty()) {
      bool committed = CommitExplicitEmbedding(runs_);
      if (committed && last_line_ended) {
        current_ = end;
        status_ = state_at_end.status_;
        sor_ = state_at_end.sor_;
        eor_ = state_at_end.eor_;
        last_ = state_at_end.last_;
        reached_end_of_line_ = state_at_end.reached_end_of_line_;
        last_before_et_ = state_at_end.last_before_et_;
        empty_run_ = state_at_end.empty_run_;
        direction_ = kOtherNeutral;
        break;
      }
    }
  }

  runs_.SetLogicallyLastRun(runs_.LastRun());
  if (reorder_runs)
    ReorderRunsFromLevels(runs_);
  end_of_run_at_end_of_line_ = Iterator();
  end_of_line_ = Iterator();

  if (!hard_line_break && runs_.RunCount() && NeedsTrailingSpace(runs_)) {
    ComputeTrailingSpace(runs_);
  }
}

template <class Iterator, class Run, class IsolatedRun>
void BidiResolver<Iterator, Run, IsolatedRun>::SetMidpointStateForIsolatedRun(
    Run& run,
    const MidpointState<Iterator>& midpoint) {
  DCHECK(!midpoint_state_for_isolated_run_.Contains(&run));
  midpoint_state_for_isolated_run_.insert(&run, midpoint);
}

template <class Iterator, class Run, class IsolatedRun>
MidpointState<Iterator>
BidiResolver<Iterator, Run, IsolatedRun>::MidpointStateForIsolatedRun(
    Run& run) {
  return midpoint_state_for_isolated_run_.Take(&run);
}

}  // namespace blink

#endif  // BidiResolver_h
