/*
Online Java - IDE, Code Editor, Compiler

Online Java is a quick and easy tool that helps you to build, compile, test your programs online.
*/

import java.util.Scanner;
import java.io.*;
import java.util.*;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

interface Playable
{
    public void assignChip(String playerName, Character chipValue);    
    public void selectChipPosition(String playerName, Character chipValue);
    public boolean checkAvailability(int rowInputAsIntegerZeroIndex, int columnInputAsIntegerZeroIndex, Character chipValue, PlayerOne plOne, PlayerTwo plTwo, String name);
    public void insertChip(int pos); 
    public boolean checkConnectFour(int row, int col, Character chipValue, String checkConnectFour);
    public void viewBoard();
}

public class Main
{
    public static void main (String[] args)
    {
        ConnectFour cf = new ConnectFour();
    }
}

class PlayerOne
{
    String[][] board;
    String playerOneName;
    String playerTwoName;
    PlayerTwo plTwo;
    
    enum Chips
    {
        NOUGHT('O'),
        CROSS('X');
            
        final Character value;
        
        Chips(Character value)
        {
            this.value=value;
        }
    }
    
    public PlayerOne(ConnectFour cf, String playerOne, String playerTwo, PlayerTwo plTwo)
    {
        this.board=board;
        this.plTwo = plTwo;
        Chips nought = Chips.NOUGHT;
        Character noughtValue = nought.value;
        this.playerOneName=playerOne;
        this.playerTwoName=playerTwo;
        cf.assignChip(playerOneName, noughtValue);    
        cf.selectChipPosition(playerOneName,noughtValue);
        cf.checkAvailability(cf.rowInputAsIntegerZeroIndex, cf.columnInputAsIntegerZeroIndex,noughtValue, this, plTwo, (playerOneName+"(Player 1)"));
        cf.checkConnectFour(cf.rowInputAsIntegerZeroIndex, cf.columnInputAsIntegerZeroIndex, noughtValue, playerOneName);
        
        plTwo = new PlayerTwo (cf, playerOneName, playerTwoName, this);
    }
}

class PlayerTwo
{
    String[][] board;
    PlayerOne plOne;
    String playerTwoName;
    String playerOneName;
    
     enum Chips
    {
        NOUGHT('O'),
        CROSS('X');
            
        final Character value;
        
        Chips(Character value)
        {
            this.value=value;
        }
    }
    
    public PlayerTwo(ConnectFour cf, String playerOne, String playerTwo, PlayerOne plOne)
    {
        this.board=board;
        Chips cross = Chips.CROSS;
        Character crossValue = cross.value;
        this.playerTwoName=playerTwo;
        this.playerOneName=playerOne;
        cf.assignChip(playerTwoName, crossValue); 
        cf.selectChipPosition(playerTwoName,crossValue);
        cf.checkAvailability(cf.rowInputAsIntegerZeroIndex,cf.columnInputAsIntegerZeroIndex,crossValue, plOne, this, (playerTwoName+"(Player 2)"));
        cf.checkConnectFour(cf.rowInputAsIntegerZeroIndex, cf.columnInputAsIntegerZeroIndex, crossValue, playerTwoName);
        plOne = new PlayerOne (cf, playerOneName,playerTwoName, this);
    }
}

class ConnectFour implements Playable
{
    int numberChipsPlaced;
    String[][] board;
    Matcher matcher;
    Matcher matcher1;
    Character colInput;
    Character rowInput;
    int columnInputAsIntegerZeroIndex;
    int rowInputAsIntegerZeroIndex;
    
    int columnChipPlacedZeroIndex;
    int rowChipPlacedZeroIndex;
    int rowChipPlaced;
    int columnChipPlaced;
    int boardTopLevel=0;
    int boardHeightZeroIndex;
    int boardWidth=3;
    int boardWidthZeroIndex = boardWidth-1;
    int sameColourTotal=0;
    int columnsFilled;
    boolean[][] gridFull = new boolean[boardWidth][boardWidth];
    boolean isAvailable;
    boolean rightColour=false;
    int offset;
    int runningTotalSameColour;
    String playerOneName;
    String playerTwoName;
    String playerNameChipValue;
    int boardHeight;
    PlayerOne plOne;
    PlayerTwo plTwo;
    String lastChipName;
    Character lastChipValue;
    boolean isGameOver = false;
    
    public boolean checkAvailability(int rowInput, int colInput, Character chipValue, PlayerOne plOne, PlayerTwo plTwo, String name)
    {
        this.plOne=plOne;
        this.plTwo=plTwo;
        System.out.println("***CHECK AVAILABILITY****" + "Board height: " +boardHeight + " board Width:" + boardWidth);
        
        //Unlike Connect4, end user would be required to specify two inputs to denote the chip (nought/cross) placement
        //We are taking their input to be similar to coordinate system X Y
        //hence this is why the indexes below on the board are back to front
        //for (int k=(boardHeightZeroIndex); k>=0; k--)
        
        //I have included this logic below since when end user specifies row 3, Java will interpret this as the lowest row on the grid
        //If end user specified row 1, Java will interpret at top row.
        //The following relationship exists   
        //User input (row 3) => Java zero index row 2   => It should be zero index row 0
        //User input (row 2) => Java zero index row 1   => It should be zero index row 1
        //User input (row 1) => Java zero index row 0   => It should be zero index row 2
        //The difference absolute value upon subtracting 2  or (boardHeight-1)
        
        System.out.println("board height: " + boardHeight);
        System.out.println("row input: " + rowInput);
        //{
            if (board[Math.abs(rowInput-(boardHeight-1))][colInput].equals("-"))
            {
                board[Math.abs(rowInput-(boardHeight-1))][colInput]= String.valueOf(chipValue);
                
                //unsure if these are required, to be commented
                //rowChipPlacedZeroIndex=k;
                //rowChipPlaced=rowChipPlacedZeroIndex+1;
                
                System.out.println("Chip: " + chipValue + " will be placed into column: " + (colInput) + " row: " + rowInput);
                lastChipName = name;
                lastChipValue = chipValue;
                numberChipsPlaced++;
                
                //if (rowChipPlaced==1)
                //{
                //    isAvailable=false;
                //}
                //else
                //{
                 //   isAvailable=true;
                  //  break;
                //}
            }
         //}
            
            //In Connect4, an invalid move would be end user selecting a column which was full
            //In noughts and crosses an invalid move will be if the coord}inate selection already has a chip
            //There is a slight different challenge now
            //There are two options to either scan each grid to see if content in there.
            //Alternatively I can keep the connect4 structure with ColumnFull BUT instead perform GridFull
            //Inline with minimal modifications, I will create a 2d boolean array gridFull
            
            
             if(gridFull[rowInput][colInput])
            {
                System.out.println("Position: [" + columnChipPlaced + "," + rowChipPlaced+"] is ALREADY TAKEN");
               
            }
            
            else if (!gridFull[rowInput][colInput])
            {
                  gridFull[rowInput][colInput]=true;
                System.out.println("Position: [" + columnChipPlaced + "," + rowChipPlaced+"] NOW HAS: " + lastChipValue);
             //   columnsFilled++;
              //  System.out.println("AVAILABILITY: Total of  " + columnsFilled + " columns filled");
              isAvailable=true;
            }
            
            
        
        //else
        //{
            System.out.println("Last chip inserted " + lastChipValue + " by: " + lastChipName);
            
        //}
        
            System.out.println("IS AVAILABLE: " + isAvailable);
           
            
               
            //similar structure to ensure same person gets turn again
            //Can potentially look to implement this with instanceOf
            if (!isAvailable)
            {
                //We can not use this and instanceof
                //since in all scenarios, this refers to connectFour class
                /*
                if (this instanceof ConnectFour)
                {
                    System.out.println("Player One instantiated from here
                    plOne = new PlayerOne (this,playerOneName,playerTwoName, plTwo);
                }
                else
                {   System.out.println("SAME PLAYER AGAIN");
                    plTwo = new PlayerTwo (this, playerOneName, playerTwoName, plOne);
                }
                */
                
            if (lastChipValue.equals('X'))
            {
                System.out.println("ALTERNATE");
                
                  plOne = new PlayerOne (this,playerOneName,playerTwoName, plTwo);
            }
            else
            {
                System.out.println("SAME PLAYER AGAIN");
                //System.out.println("again one");
                plTwo = new PlayerTwo (this, playerOneName, playerTwoName, plOne);
            }
            }
            
            viewBoard();
            isAvailable=false;
        return true;
            
        } //end method
        
    //}  //end of class 
    
    public void insertChip(int pos)
    {
        System.out.println("***INSERT CHIP****");
    }
    
    public boolean checkConnectFour(int rowChipPlacedZeroIndex, int columnChipPlacedZeroIndex, Character chipValue, String playerName)
    {
        System.out.println("***CHECK TIC-TAC-TOE****");
        
        playerNameChipValue = playerName + "("+chipValue+")";


        //Same principle, we know the two row is 0 and bottom row is 5 in connect4 (zero index)
        //we know the two row is 0 and bottom row is 2 in nought crosses (zero index)
        //We are still using same techniques to add the totals for downwards and upward
        
        //boardHeightZeroIndex = board.length-1    =  2
        //counting rows top to bottom
       
        if (rowChipPlacedZeroIndex!=2)
        {
            System.out.println("VERTICAL CHECK => DOWNWARDS*****runningTotal: " + runningTotalSameColour);
            System.out.println(chipValue);
            
            for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            {
                if (board[count][columnChipPlacedZeroIndex].equals(String.valueOf(chipValue)))
                {
                    sameColourTotal++;
                    
                    if (sameColourTotal==3)
                    {
                        System.out.println("***1Congratulations " + playerNameChipValue + " Connect 4 in column:" + columnChipPlaced);
                        
                        isGameOver=true;
                        System.exit(0);
                    }
                }
            }
            runningTotalSameColour=sameColourTotal;
            sameColourTotal=0;
        }
        
        System.out.println("row: " + rowChipPlacedZeroIndex);
        
        if (rowChipPlacedZeroIndex!=0)
        {
            //for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            System.out.println("VERTICAL CHECK => UPWARDS***** runningTotal: " + runningTotalSameColour);
            for (int count = rowChipPlacedZeroIndex; count>=boardTopLevel; count--)
            {
                if (board[count][columnChipPlacedZeroIndex].equals(String.valueOf(chipValue)))
                {
                    sameColourTotal++;
                    
                    if (sameColourTotal+runningTotalSameColour>=4)
                    //if (sameColourTotal==3)
                    {
                        System.out.println("***1Congratulations " + playerNameChipValue + " Connect 4 in column:" + columnChipPlaced);
                        isGameOver=true;
                        System.exit(0);
                    }
                }
            }
            //runningTotalSameColour=sameColourTotal;
            sameColourTotal=0;
            offset=0;
            runningTotalSameColour=0;
            //rightColour=false;
        }
        
        
        
        
        if (columnChipPlacedZeroIndex!=boardWidthZeroIndex)
        {
            System.out.println("HORIZONTAL CHECK => RIGHT*****runningTotal: " + runningTotalSameColour);
            for (int count = columnChipPlacedZeroIndex; count<=boardWidthZeroIndex; count++)
            {
                if (board[rowChipPlacedZeroIndex][count].equals(String.valueOf(chipValue)))
                {
                    sameColourTotal++;
                    //rightColour=true;
                    
                    if (sameColourTotal==4)
                    {
                        System.out.println("***2Congratulations " +  playerNameChipValue + "  Connect 4 in row:" + rowChipPlaced);
                        isGameOver=true;
                        System.exit(0);
                        //return true;
                        
                    }
                }
            }
            
            runningTotalSameColour=sameColourTotal;
            sameColourTotal=0;
        }
        
        
        
        if (columnChipPlacedZeroIndex!=0)
        {
            System.out.println("HORIZONTAL CHECK => LEFT*****runningTotal: " + runningTotalSameColour);
            System.out.println(chipValue);
            for (int count = columnChipPlacedZeroIndex; count>=0; count--)
            {
                if (board[rowChipPlacedZeroIndex][count].equals(String.valueOf(chipValue)))
                {
                    sameColourTotal++;
                    System.out.println("running total: " + runningTotalSameColour);
                     System.out.println("same colour total: " + sameColourTotal);
                    
                    if (sameColourTotal+runningTotalSameColour>=4)
                    {
                        System.out.println("***3Congratulations " + playerNameChipValue + "  Connect 4 in row:" + rowChipPlaced);
                        isGameOver=true;
                        System.exit(0);
                        //return true;
                    }
                    
                }
            }
            offset=0;
            sameColourTotal=0;
            runningTotalSameColour=0;
            //rightColour=false;
        }
        
        //There are actually more grids from which diagonal checks are not applicable due to the size of the 3x3 board
        //But will leave all logic intact
        System.out.println("DIAGONAL CHECK 1");  
    
    //boardWidthZeroIndex=2
    
        if (columnChipPlacedZeroIndex!=boardWidthZeroIndex && rowChipPlacedZeroIndex!=boardTopLevel 
        && ((columnChipPlaced==1 && rowChipPlaced==1) || (columnChipPlaced==2 && rowChipPlaced==2)))
        {
            System.out.println("INSIDE D1 - Diagonal north east check"+ "runningTotal: " + runningTotalSameColour);
            
            for (int count = rowChipPlacedZeroIndex; count>=0; count--)
            {
                if (columnChipPlacedZeroIndex+offset<=boardWidthZeroIndex)
                {
                    if (board[count][columnChipPlacedZeroIndex+offset].equals(String.valueOf(chipValue)))
                    {
                        //rightColour=true;
                        sameColourTotal++;
                        offset++;
                        
                        if (sameColourTotal==3)
                        {
                            System.out.println("***5Congratulations " + playerNameChipValue + "  Connect 4 in diagonal");
                            isGameOver=true;
                            System.exit(0);
                            //return true;
                            
                        }
                    }
                    
                }
            }
            runningTotalSameColour=sameColourTotal;
            sameColourTotal=0;
            offset=0;
        }
        
        System.out.println("DIAGONAL CHECK 2"); 
        
        
                                                                         
        if (columnChipPlacedZeroIndex!=0 && rowChipPlacedZeroIndex!=boardHeightZeroIndex 
        && ((columnChipPlaced==3 && rowChipPlaced==3) || (columnChipPlaced==2 && rowChipPlaced==2)))
        {
            System.out.println("INSIDE D2 - Diagonal south west check"+ "runningTotal: " + runningTotalSameColour);
            
            for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            {
                if (columnChipPlacedZeroIndex-offset>=0)
                {
                    if (board[count][columnChipPlacedZeroIndex-offset].equals(String.valueOf(chipValue)))
                    {
                        sameColourTotal++;
                        offset++;
                        
                        
                        if (sameColourTotal+runningTotalSameColour>=4)
                        {
                            System.out.println("***6Congratulations " + playerNameChipValue + "  Connect 4 in diagonal");
                            isGameOver=true;
                            System.exit(0);
                            //return true;
                        }
                        
                        //if (sameColourTotal+runningTotalSameColour==5 & rightColour)
                        //{
                         //   System.out.println("***7Congratulations" + playerNameChipValue + "  Connect 4 in diagonal");
                          //  isGameOver=true;
                        //    return true;
                        //}
                    }
                    
                }
            }
            runningTotalSameColour=0;
            //rightColour=false;
            sameColourTotal=0;
            offset=0;
        }
        System.out.println("DIAGONAL CHECK 3"); 
                                      
                                    
                                      
        if (columnChipPlacedZeroIndex!=boardWidthZeroIndex && rowChipPlacedZeroIndex!=boardHeightZeroIndex 
        && ((columnChipPlaced==1 && rowChipPlaced==3) || (columnChipPlaced==2 && rowChipPlaced==2)))
        {
            System.out.println("INSIDE D3 - Diagonal south east check"+ "runningTotal: " + runningTotalSameColour);
                                                        
            for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            {
                if (columnChipPlacedZeroIndex+offset<=boardWidthZeroIndex)               
                {
                    if (board[count][columnChipPlacedZeroIndex+offset].equals(String.valueOf(chipValue)))
                    {
                        //rightColour=true;
                        sameColourTotal++;
                        offset++;
                        System.out.println("value of offset: " + offset);
                    
                        if (sameColourTotal==3)
                        {
                            System.out.println("***8Congratulations " + playerNameChipValue + "  Connect 4 in diagonal");
                            isGameOver=true;
                            System.exit(0);
                            //return true;
                            
                        }
                    }
                    
                }
            }
            runningTotalSameColour=sameColourTotal;
            sameColourTotal=0;
            offset=0;
        }
        
        System.out.println("DIAGONAL CHECK 4"); 
      
         
        
        if (columnChipPlacedZeroIndex!=0 && rowChipPlacedZeroIndex!=0 
        && ((columnChipPlaced==3 && rowChipPlaced==1) || (columnChipPlaced==2 && rowChipPlaced==2)))
        {
            System.out.println("INSIDE D4 - Diagonal north west check"+ "runningTotal: " + runningTotalSameColour);
                
            for (int count = rowChipPlacedZeroIndex; count>=0; count--)
            {
                if (columnChipPlacedZeroIndex-offset>=0)
                {
                    if (board[count][columnChipPlacedZeroIndex-offset].equals(String.valueOf(chipValue)))
                    {
                        sameColourTotal++;
                        offset++;
                            
                        if (sameColourTotal+runningTotalSameColour>=4)
                        {
                            System.out.println("***9Congratulations " + playerNameChipValue + "  Connect 4 in diagonal");
                            isGameOver=true;
                            System.exit(0);
                            //return true;
                            
                        }
                        
                    }
                }
            }
            //rightColour=false;
            runningTotalSameColour=0;
            sameColourTotal=0;
            offset=0;
        }
        
        //We need a different technique to denote full board.
        //It is much simpler now, after 9 moves
        //
        if (numberChipsPlaced==9)
        {
             //if (columnsFilled==boardWidth)
            //{
                System.out.println("GAME OVER - NO WINNER");
                System.exit(0);
            //}
        }
        
        return false;
    }
    
    public void configurePlayers()
    {
        Scanner input = new Scanner(System.in);
        System.out.println("Enter name for Player 1:");
        playerOneName = input.next();
        System.out.println("Enter name for Player 2:");
        playerTwoName = input.next();
    }
    
    public void assignChip(String playerName, Character chip)
    {
        System.out.println("***ASSIGN CHIP****");
        System.out.println(playerName + " has been assigned: " + chip + " chip");
    }
    
    public void selectChipPosition(String playerName, Character chip)
    {
        System.out.println("***SELECT CHIP POSITION****");
        Scanner in = new Scanner(System.in);
        
        viewBoard();
        
        do
        {
            System.out.println(playerName + "("+chip+")" +  ",  Which column would you like to insert the chip?");
            colInput = in.next().charAt(0);
            
            System.out.println(playerName + "("+chip+")" +  ",  Which row would you like to insert the chip?");
            rowInput = in.next().charAt(0);
        
            String regexColumns="[123]";
            Pattern pattern = Pattern.compile(regexColumns, Pattern.CASE_INSENSITIVE);
            matcher = pattern.matcher(Character.toString(colInput));
            matcher1 = pattern.matcher(Character.toString(rowInput));
            
        }while (!matcher.find() && !matcher1.find());
        
        columnInputAsIntegerZeroIndex = Character.getNumericValue(colInput-1);
        rowInputAsIntegerZeroIndex = Character.getNumericValue(rowInput-1);
        
        columnChipPlaced = Character.getNumericValue(colInput);
        rowChipPlaced = Character.getNumericValue(rowInput);
    }
    
    public void viewBoard()
    {
        int j=0;
        int rowOnBoard=0;
        
        if (isGameOver)
        {
            System.out.println("\n****CURRENT BOARD****: " + "***Congratulations " + playerNameChipValue);
        }
        else
        {
            System.out.println("\n****CURRENT BOARD****");
        }
        
        StringBuilder [] sb  = new StringBuilder[8];
        
        for (int i=0; i<sb.length;i++)
        {
            if (i%2==0)
            {
                sb[i+j] =   new StringBuilder("|---|---|---|");
            }
            else
            {
                sb[i]=new StringBuilder("");
                
                if (rowOnBoard!=board.length)
                {
                    for (int m=0; m<board[0].length;m++)
                    {
                        if (m==0)
                        {
                            sb[i].append("|"+" " +board[rowOnBoard][m]+ " |");
                        }
                        else
                        {
                            sb[i].append(""+" "+board[rowOnBoard][m]+ " |");
                        }
                    }
                    rowOnBoard++;
                }
            }
        }
        for (StringBuilder gg: sb)
        {
            System.out.println(gg.toString());
        }
    }
    
    public ConnectFour() 
    {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        System.out.println("*********Welcome to TIC-TAC-TOE**************");
        System.out.println("NOTE: Code will not terminate on a win, see onscreen messages");
        
        board = new String [][]   { {"-", "-", "-"},
                                    {"-", "-", "-"},
                                    {"-", "-", "-"}                            
                                  };
        
        boardHeightZeroIndex = board.length-1;
        boardHeight=board.length;
        
        viewBoard();
        configurePlayers();
        PlayerOne plOne = new PlayerOne (this, playerOneName,playerTwoName, plTwo);
    }
}