// Copyright 2015 The Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package markdown

import "strings"

const (
	ptRoot = iota
	ptList
	ptBlockQuote
	ptParagraph
	ptReference
)

type StateBlock struct {
	StateCore

	BMarks     []int // offsets of the line beginnings
	EMarks     []int // offsets of the line endings
	TShift     []int // indents for each line
	SCount     []int
	BSCount    []int
	BlkIndent  int  // required block content indent (in a list etc.)
	Line       int  // line index in the source string
	LineMax    int  // number of lines
	Tight      bool // loose or tight mode for lists
	ParentType byte // parent block type
	Level      int
}

func (s *StateBlock) IsLineEmpty(n int) bool {
	return s.BMarks[n]+s.TShift[n] >= s.EMarks[n]
}

func (s *StateBlock) SkipEmptyLines(from int) int {
	for from < s.LineMax && s.IsLineEmpty(from) {
		from++
	}
	return from
}

func (s *StateBlock) SkipSpaces(pos int) int {
	src := s.Src
	for pos < len(src) && byteIsSpace(src[pos]) {
		pos++
	}
	return pos
}

func (s *StateBlock) SkipBytes(pos int, b byte) int {
	src := s.Src
	for pos < len(src) && src[pos] == b {
		pos++
	}
	return pos
}

func (s *StateBlock) SkipBytesBack(pos int, b byte, min int) int {
	for pos > min {
		pos--
		if s.Src[pos] != b {
			return pos + 1
		}
	}
	return pos
}

func (s *StateBlock) SkipSpacesBack(pos int, min int) int {
	for pos > min {
		pos--
		if !byteIsSpace(s.Src[pos]) {
			return pos + 1
		}
	}
	return pos
}

func (s *StateBlock) Lines(begin, end, indent int, keepLastLf bool) string {
	if begin == end {
		return ""
	}

	src := s.Src

	queue := make([]string, end-begin)

	for i, line := 0, begin; line < end; i, line = i+1, line+1 {
		lineIndent := 0
		lineStart := s.BMarks[line]
		first := lineStart
		last := s.EMarks[line]
		if (line+1 < end || keepLastLf) && last < len(src) {
			last++
		}

		for first < last && lineIndent < indent {
			ch := src[first]

			if byteIsSpace(ch) {
				if ch == '\t' {
					lineIndent += 4 - (lineIndent+s.BSCount[line])%4
				} else {
					lineIndent++
				}
			} else if first-lineStart < s.TShift[line] {
				lineIndent++
			} else {
				break
			}

			first++
		}

		if lineIndent > indent {
			queue[i] = strings.Repeat(" ", lineIndent-indent) + src[first:last]
		} else {
			queue[i] = src[first:last]
		}
	}

	return strings.Join(queue, "")
}

func (s *StateBlock) PushToken(tok Token) {
	tok.SetLevel(s.Level)
	s.Tokens = append(s.Tokens, tok)
}

func (s *StateBlock) PushOpeningToken(tok Token) {
	tok.SetLevel(s.Level)
	s.Level++
	s.Tokens = append(s.Tokens, tok)
}

func (s *StateBlock) PushClosingToken(tok Token) {
	s.Level--
	tok.SetLevel(s.Level)
	s.Tokens = append(s.Tokens, tok)
}