package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

type CMFile struct {
	name    string
	fp      io.ReadCloser
	rd      *bufio.Reader
	lineCnt int64
	arg     *CMArgs
}

func (cf *CMFile) Close() {
	cf.fp.Close()
}

func (cf *CMFile) WrapError(err error) error {
	if err == nil {
		return nil
	}
	if err == io.EOF {
		return err
	}
	return fmt.Errorf("file(%s:%d) %w", cf.name, cf.lineCnt, err)
}

func (cf *CMFile) readLine() (data Line, err error) {
	eol := cf.arg.eol
	var rd = cf.rd
	var line []byte
	for {
		// rd.ReadLine()
		line, err = rd.ReadSlice(eoxLast(eol))
		if err != nil {
			if err == bufio.ErrBufferFull {
				data.write(line)
				continue
			}
			if err != io.EOF {
				return Line{}, cf.WrapError(err)
			}
			// io.EOF
			LogInfo("readLine was eof")
			if len(line) == 0 {
				break
			}
			// len(line) > 0,process.
		}
		// check eol;
		data.write(line)
		cache := data.bytes()
		if checkEOX(eol, cache) {
			// write new line;
			data.buf.Reset()
			data.write(trimEOX(eol, cache))
			// data.write(line)
		} else {
			if err == io.EOF {
				// write last line;
				// data.write(line)
			} else {
				// not finish line,continue;
				// data.write(line)
				continue
			}
		}
		if data.len() == 0 {
			// empty line;read next line
			continue
		}
		break
	}
	//data.arg = cf.arg
	data.fname = cf.name
	data.lineIdx = cf.lineCnt
	return data, cf.WrapError(err)
}

func trimEOX(eox, line []byte) []byte {
	return line[:len(line)-len(eox)]
}

func checkEOX(eox, line []byte) bool {
	el := len(eox)
	ll := len(line)
	if el > ll {
		return false
	}
	return bytes.Equal(eox, line[ll-el:])
}

func eoxLast(eox []byte) byte {
	l := len(eox)
	return eox[l-1]
}

type Line struct {
	buf bytes.Buffer
	//arg     *CMArgs
	lineIdx int64
	fname   string
}

// func (l *Line) writeTrim(p []byte) int {
// 	return l.write(bytes.TrimSpace(p))
// }
// 由于数据可以是有空格之类的任何格式数据，所以这里不应该trim.
func (l *Line) write(p []byte) int {
	n, _ := l.buf.Write(p)
	return n
}

func (l *Line) writeByte(c byte) {
	l.buf.WriteByte(c)
}

func (l *Line) len() int {
	return l.buf.Len()
}

func (l *Line) bytes() []byte {
	return l.buf.Bytes()
}

func (l *Line) splitPK(eow []byte) (pk, sub []byte, err error) {
	var b []byte
	//eow := l.arg.eow
	d := eoxLast(eow)
	for {
		b, err = l.buf.ReadBytes(d)
		if err != nil {
			if err != io.EOF {
				err = l.wrapError(err)
				return
			}
		}
		pk = append(pk, b...)
		if len(pk) == 0 && err == io.EOF {
			return pk, nil, err
		}
		if !checkEOX(eow, pk) {
			continue
		} else {
			pk = trimEOX(eow, pk)
			break
		}
	}
	//
	return pk, l.buf.Bytes(), nil
}

func (l *Line) wrapError(err error) error {
	if err == nil {
		return nil
	}
	if err == io.EOF {
		return err
	}
	return fmt.Errorf("line(%s:%d) %w", l.fname, l.lineIdx, err)
}
