package org.mian.gitnex.views;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.mian.gitnex.core.MainGrammarLocator;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.codeeditor.markwon.SyntaxHighlighter;
import org.mian.gitnex.helpers.codeeditor.theme.Theme;

/**
 * @author opyale
 */
public class SyntaxHighlightedArea extends LinearLayout {

	private Theme theme;

	private TextView sourceView;
	private LinesView linesView;

	public SyntaxHighlightedArea(@NonNull Context context) {
		super(context);
		setup();
	}

	public SyntaxHighlightedArea(@NonNull Context context, @Nullable AttributeSet attrs) {
		super(context, attrs);
		setup();
	}

	public SyntaxHighlightedArea(
			@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		setup();
	}

	public void setup() {

		theme = Theme.getDefaultTheme(getContext());

		sourceView = new TextView(getContext());

		sourceView.setLayoutParams(
				new ViewGroup.LayoutParams(
						ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
		sourceView.setTypeface(
				Typeface.createFromAsset(
						getContext().getAssets(), "fonts/sourcecodeproregular.ttf"));
		sourceView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
		sourceView.setTextColor(
				getContext().getResources().getColor(theme.getDefaultColor(), null));
		sourceView.setTextIsSelectable(true);

		int padding = AppUtil.getPixelsFromDensity(getContext(), 5);
		sourceView.setPadding(padding, 0, padding, 0);

		HorizontalScrollView horizontalScrollView = new HorizontalScrollView(getContext());
		horizontalScrollView.setLayoutParams(
				new LinearLayout.LayoutParams(
						ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
		horizontalScrollView.addView(sourceView);

		linesView = new LinesView(getContext());

		linesView.setLayoutParams(
				new LinearLayout.LayoutParams(
						ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
		linesView.setPadding(
				AppUtil.getPixelsFromDensity(getContext(), 3),
				0,
				AppUtil.getPixelsFromDensity(getContext(), 6),
				0);

		linesView.getPaint().setTypeface(sourceView.getTypeface());
		linesView.getPaint().setTextSize(sourceView.getTextSize());

		linesView.setBackgroundColor(
				getContext().getResources().getColor(theme.getBackgroundColor(), null));
		linesView.setTextColor(getContext().getResources().getColor(theme.getDefaultColor(), null));
		linesView.setLineColor(getContext().getResources().getColor(theme.getDefaultColor(), null));

		setOrientation(HORIZONTAL);
		setBackgroundColor(getContext().getResources().getColor(theme.getBackgroundColor(), null));
		addView(linesView);
		addView(horizontalScrollView);
	}

	public void setContent(@NonNull String source, @NonNull String extension) {

		if (source.length() > 0) {

			Thread highlightingThread =
					new Thread(
							() -> {
								try {

									CharSequence highlightedSource =
											SyntaxHighlighter.create(
															getContext(),
															theme,
															MainGrammarLocator
																	.DEFAULT_FALLBACK_LANGUAGE)
													.highlight(
															MainGrammarLocator.fromExtension(
																			extension)
																	.toUpperCase(),
															source);

									getActivity()
											.runOnUiThread(
													() -> sourceView.setText(highlightedSource));

								} catch (Throwable ignored) {
									// Fall back to plaintext if something fails
									getActivity().runOnUiThread(() -> sourceView.setText(source));
								}
							});

			Thread lineCountingThread =
					new Thread(
							() -> {
								long lineCount = AppUtil.getLineCount(source);

								try {
									highlightingThread.join();
								} catch (InterruptedException ignored) {
								}

								getActivity()
										.runOnUiThread(() -> linesView.setLineCount(lineCount));
							});

			highlightingThread.start();
			lineCountingThread.start();
		}
	}

	private Activity getActivity() {
		return (Activity) getContext();
	}

	public String getContent() {
		return sourceView.getText().toString();
	}

	private static class LinesView extends View {

		private final Paint paint = new Paint();
		private final Rect textBounds = new Rect();

		@ColorInt private int backgroundColor;
		@ColorInt private int textColor;
		@ColorInt private int lineColor;

		private long lineCount;

		public LinesView(Context context) {
			super(context);
		}

		public void setLineCount(long lineCount) {
			this.lineCount = lineCount;
			requestLayout();
		}

		@Override
		public void setBackgroundColor(@ColorInt int backgroundColor) {
			this.backgroundColor = backgroundColor;
			invalidate();
		}

		public void setTextColor(@ColorInt int textColor) {
			this.textColor = textColor;
			invalidate();
		}

		public void setLineColor(@ColorInt int lineColor) {
			this.lineColor = lineColor;
			invalidate();
		}

		public Paint getPaint() {
			return paint;
		}

		@Override
		protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

			String highestLineNumber = String.valueOf(lineCount);

			paint.getTextBounds(highestLineNumber, 0, highestLineNumber.length(), textBounds);

			setMeasuredDimension(
					getPaddingLeft() + textBounds.width() + getPaddingRight(),
					MeasureSpec.getSize(heightMeasureSpec));
		}

		@Override
		protected void onDraw(Canvas canvas) {

			paint.setColor(backgroundColor);

			canvas.drawRect(0, 0, getWidth(), getHeight(), paint);

			float marginTopBottom = (float) (getHeight() - (textBounds.height() / 2)) / lineCount;

			paint.setColor(textColor);

			canvas.save();
			canvas.translate(getPaddingLeft(), marginTopBottom);

			for (int currentLine = 1; currentLine <= lineCount; currentLine++) {

				canvas.drawText(String.valueOf(currentLine), 0, 0, paint);
				canvas.translate(0, marginTopBottom);
			}

			paint.setColor(lineColor);

			int dividerX = getWidth() - 1;
			int dividerY = getHeight();

			canvas.restore();
			canvas.drawLine(dividerX, 0, dividerX, dividerY, paint);
		}
	}
}
