/*
Online Java - IDE, Code Editor, Compiler

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

//I have made slightly harder work since I kept
//variables to use in zero index world such as navigating the board
//and also non zero index, which I feel would be better to end user
//when providing information about columns...

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 inputAsIntegerZeroIndex, Character chipValue);
    public void insertChip(int pos); 
    public boolean checkConnectFour(int pos, int columnChipPlacedZeroIndex, Character chipValue, String checkConnectFour);
    public void viewBoard();
}

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



class PlayerOne
{
    String[][] board;
    PlayerTwo plTwo;
    String playerOneName;
    String playerTwoName;
    
     enum Chips
    {
        RED('O'),
        YELLOW('X');
            
        final Character value;
        
        Chips(Character value)
        {
            this.value=value;
        }
    }
    
    public PlayerOne(ConnectFour cf, String playerOne, String playerTwo)
    {
        this.board=board;
        Chips yellow = Chips.YELLOW;
        Character yellowValue = yellow.value;
        this.playerOneName=playerOne;
        this.playerTwoName=playerTwo;
        
       cf.assignChip(playerOneName, yellowValue);    
        
        
        cf.selectChipPosition(playerOneName,yellowValue);
        
        cf.checkAvailability(cf.inputAsIntegerZeroIndex,yellowValue);
        cf.checkConnectFour(cf.inputAsIntegerZeroIndex, cf.rowChipPlacedZeroIndex, yellowValue, playerOneName);
        
        //Note players do not need instance variables of each other,
        //no specific variables are being written in their respective classes
        plTwo = new PlayerTwo (cf, playerOneName, playerTwoName);
        
    }
}

class PlayerTwo
{
    String[][] board;
    PlayerOne plOne;
    String playerTwoName;
    String playerOneName;
    
    
     enum Chips
    {
        RED('O'),
        YELLOW('X');
            
        final Character value;
        
        Chips(Character value)
        {
            this.value=value;
        }
    }
    
    public PlayerTwo(ConnectFour cf, String playerOne, String playerTwo)
    {
        this.board=board;
        Chips red = Chips.RED;
        Character redValue = red.value;
        this.playerTwoName=playerTwo;
        this.playerOneName=playerOne;
        
        cf.assignChip(playerTwoName, redValue); 
        
        
        cf.selectChipPosition(playerTwoName,redValue);
        
        cf.checkAvailability(cf.inputAsIntegerZeroIndex,redValue);
        cf.checkConnectFour(cf.inputAsIntegerZeroIndex, cf.rowChipPlacedZeroIndex, redValue, playerTwoName);
        
        //Note players do not need instance variables of each other,
        //no specific variables are being written in their respective classes
        plOne = new PlayerOne (cf, playerOneName,playerTwoName);
        
    }
}
//----------------------------------------


class ConnectFour implements Playable
{
    String[][] board;
    Matcher matcher;
    Character input;
    int inputAsIntegerZeroIndex;
    int columnChipPlacedZeroIndex;
    int rowChipPlacedZeroIndex;
    int rowChipPlaced;
    int columnChipPlaced;
    int boardBaseLevel=0;
    int boardHeightZeroIndex;
    int boardWidth=7;
    int boardWidthZeroIndex = boardWidth-1;
    int sameColour=0;
    int columnsFilled;
    boolean columnFull[] = new boolean[boardWidth];
    boolean availability;
    boolean rightColour=false;
    int offset;
    int runningTotalSameColour;
    String playerOneName;
    String playerTwoName;
    String playerNameChipValue;
    
    public boolean checkAvailability(int input, Character chipValue)
    {
        System.out.println("***CHECK AVAILABILITY****");
        
        for (int k=(boardHeightZeroIndex); k>=0; k--)
        {
            //System.out.println("value of k:" + k);
            
            if (board[k][input].equals("-"))
            {
                board[k][input]= String.valueOf(chipValue);
                rowChipPlacedZeroIndex=k;
                rowChipPlaced=rowChipPlacedZeroIndex+1;
                
                System.out.println("Chip: " + chipValue + " will be placed into column: " + (columnChipPlaced) + " row: " + rowChipPlaced);
                availability=true;
                break;
           }
           
        }
        
        //if no availability
        //there are lots of possibilities of completing this.
        //I prefer keeping track at the column level
        
        if (!availability)
        {
               System.out.println("Column: " + columnChipPlaced + " is full. Try alternate");
               
               //if it is currently not marked as full
               //it sets it and increases columnsFilled
               if(!columnFull[columnChipPlacedZeroIndex])
               {
               columnFull[columnChipPlacedZeroIndex]=true;
               columnsFilled++;
               }
               
               if (columnsFilled==boardWidth)
               {
                   System.out.println("GAME OVER - NO WINNER");
                   System.exit(0);
               }
               
               PlayerOne plOne = new PlayerOne (this,playerOneName,playerTwoName);
        }
        viewBoard();
        
        return true;
    };
    
    public void insertChip(int pos)
    {
        System.out.println("***INSERT CHIP****");
    }
    
    //pos is the column,  row is the row
    public boolean checkConnectFour(int pos, int row, Character chipValue, String playerName)
    {
        System.out.println("***CHECK CONNECT FOUR****");
        
        playerNamechipValue = playerName + "("+playerName+")";
        
        //need to check vertically first above and below current position
        //in order to check vertically
        
       // rowChipPlacedZeroIndex
        
        //can only check below if rowChipPlacedZeroIndex!=5
        //can only check above if rowChipPlacedZeroIndex!=(board.length-1)
        
        //System.out.println("VERTICAL CHECK******");
        
        //will not perform if on bottom row
        if (rowChipPlacedZeroIndex!=5)
        {
            //System.out.println("in here");
            //System.out.println(rowChipPlacedZeroIndex);
            //System.out.println(rowChipPlaced);
            
            //we know its the highest coin in the column,
            //so only need to check downwards.
            //if another chip added on top, it would follow same
            for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            {
                //System.out.println("in row: " + count);
                //System.out.println("chip: " + String.valueOf(chipValue));
                //System.out.println("ff:" + board[count][columnChipPlacedZeroIndex]);
                
                if (board[count][columnChipPlacedZeroIndex].equals(String.valueOf(chipValue)))
                {
                    sameColour++;
                    //System.out.println("sc:" + sameColour);
                    
                    if (sameColour==4)
                    {
                        System.out.println("***1Congratulations " + playerNamechipValue + " Connect 4 in column:" + columnChipPlaced);
                        break;
                        //return true;
                    }
                    
                }
                else
                {
                    sameColour=0;
                    break;
                }
            }
        }
        
            //System.out.println("MUST BE HERE");
            //checking horizontally to right, can not perform if on last column
            if (columnChipPlacedZeroIndex!=boardWidthZeroIndex)
            {
            
            for (int count = columnChipPlacedZeroIndex; count<=boardWidthZeroIndex; count++)
            {
                //System.out.println("in row: " + rowChipPlacedZeroIndex);
                //System.out.println("chip: " + String.valueOf(chipValue));
                
                
                if (board[rowChipPlacedZeroIndex][count].equals(String.valueOf(chipValue)))
                {
                    sameColour++;
                    //System.out.println("sc:" + sameColour);
                    rightColour=true;
                    
                    if (sameColour==4)
                    {
                        System.out.println("***2Congratulations " +  playerNamechipValue + "  Connect 4 in row:" + rowChipPlaced);
                        
                        return true;
                    }
                    
                }
                else
                {
                    //need be careful here since opposite player chip can
                    //appear on right horizontal, but there might be sufficient
                    //player colours on left horizontal to win.
                    //note these loops are doing counts relative to the chip placed.
                    //in the stack situation, this does not come into play
                    //since opposition colour in the stack would disrupt the linearity
                    //to win the game
                    //so it is important to keep track of the total stored in 
                    //sameColour in a new variable (runningTotalSameColour)
                    //and once it has finished the left examination, it can remove contents of
                    //runningTotalSameColour
                    
                    runningTotalSameColour=sameColour;
                    sameColour=0;
                    break;
                }
            }
            }
            
            //System.out.println("MUST BE HERE BOW");
            //-----------------------------
            
             //checking horizontally to left, can not perform if on first column
             //also need to ensure that the chip placed is not counted again!
            if (columnChipPlacedZeroIndex!=0)
            {
                //System.out.println(columnChipPlacedZeroIndex);
            
            for (int count = columnChipPlacedZeroIndex; count>=0; count--)
            {
                //System.out.println("in row: " + rowChipPlacedZeroIndex);
                //System.out.println("chip: " + String.valueOf(chipValue));
                
                if (board[rowChipPlacedZeroIndex][count].equals(String.valueOf(chipValue)))
                {
                
                //we know it has found chips on the right hand side already
                
                    sameColour++;
                    //System.out.println("sc:" + sameColour);
                    
                    if (sameColour+runningTotalSameColour==4 & !rightColour)
                    {
                        System.out.println("***3Congratulations " + playerNamechipValue + "  Connect 4 in row:" + rowChipPlaced);
                        
                        return true;
                    }
                    
                    //need to get an additional since it has counted both left
                    //and right and includes the chip placed in both totals
                    if (sameColour+runningTotalSameColour==5 & rightColour)
                    {
                        System.out.println("***4Congratulations " + playerNamechipValue + "  Connect 4 in row:" + rowChipPlaced);
                        return true;
                    }
                }
                else
                {
                    //all set back to defaults
                    offset=0;
                    sameColour=0;
                    runningTotalSameColour=0;
                    rightColour=false;
                    break;
                }
            }
            
            }
            
          System.out.println("DIAGONAL CHECK 1");  
            
        //most trickiest, it has to examine 4 diagonals.
        //concept similar to horizontal
        
        //diagonal NE (coordinates)
        //it can not perform if on top row
        //or last column board
            if (columnChipPlacedZeroIndex!=boardWidthZeroIndex && rowChipPlacedZeroIndex!=boardBaseLevel)
            {
                System.out.println("INSIDE D1 - Diagonal north east check");
                //System.out.println(columnChipPlacedZeroIndex);
            
            //it has to scan one column further and one row upward (going toward 0 row index)
            for (int count = rowChipPlacedZeroIndex; count>=0; count--)
            {
                //System.out.println("in row: " + rowChipPlacedZeroIndex);
                //System.out.println("chip: " + String.valueOf(chipValue));
                
                if (board[count][columnChipPlacedZeroIndex+offset].equals(String.valueOf(chipValue)))
                {
                    rightColour=true;
                    sameColour++;
                    offset++;
                    //System.out.println("sc:" + sameColour);
                    
                    if (sameColour==4)
                    {
                        System.out.println("***5Congratulations " + playerNamechipValue + "  Connect 4 in diagonal");
                        return true;
                    }
                    
                }
                else
                {
                    runningTotalSameColour=sameColour;
                    sameColour=0;
                    offset=0;
                    break;
                }
            }
            }
            
            //--------------------
            
            //it has to scan one column left and and one row downward
            //not possible from bottom row or most right column
            //SW coordinates
            System.out.println("DIAGONAL CHECK 2"); 
            
            if (columnChipPlacedZeroIndex!=0 && rowChipPlacedZeroIndex!=boardBaseLevel)
            {
                System.out.println("INSIDE D2 - Diagonal south west check");
            
            for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            {
                //System.out.println("in row: " + rowChipPlacedZeroIndex);
                //System.out.println("chip: " + String.valueOf(chipValue));
                
                if (board[count][columnChipPlacedZeroIndex-offset].equals(String.valueOf(chipValue)))
                {
                    sameColour++;
                    offset++;
                    //System.out.println("sc:" + sameColour);
                    
                    if (sameColour+runningTotalSameColour==4 & !rightColour)
                    {
                        System.out.println("***6Congratulations " + playerNamechipValue + "  Connect 4 in diagonal");
                        return true;
                    }
                    
                    //need to get an additional since it has counted both left
                    //and right and includes the chip placed in both totals
                    if (sameColour+runningTotalSameColour==5 & rightColour)
                    {
                        System.out.println("***7Congratulations" + playerNamechipValue + "  Connect 4 in diagonal");
                        return true;
                    }
                }
                else
                {
                    runningTotalSameColour=0;
                    rightColour=false;
                    sameColour=0;
                    offset=0;
                    break;
                }
            }
            
            }
        
        //it now has to scan SE  (right movement on board)
        //it can not perform from following locations
        //column 5 and row 5
        
        System.out.println("DIAGONAL CHECK 3"); 
        
          if (columnChipPlacedZeroIndex!=boardWidthZeroIndex && rowChipPlacedZeroIndex!=boardHeightZeroIndex)
          {
              System.out.println("INSIDE D3 - Diagonal south east check");
              //System.out.println(columnChipPlacedZeroIndex);
            
            //it has to scan one column further and one row upward (going toward 0 row index)
            for (int count = rowChipPlacedZeroIndex; count<=boardHeightZeroIndex; count++)
            {
                //System.out.println("in row: " + rowChipPlacedZeroIndex);
                //System.out.println("chip: " + String.valueOf(chipValue));
                
                if (board[count][columnChipPlacedZeroIndex+offset].equals(String.valueOf(chipValue)))
                {
                    rightColour=true;
                    sameColour++;
                    offset++;
                    //System.out.println("sc:" + sameColour);
                    
                    if (sameColour==4)
                    {
                        System.out.println("***8Congratulations " + playerNamechipValue + "  Connect 4 in diagonal");
                        return true;
                    }
                    
                }
                else
                {
                    runningTotalSameColour=sameColour;
                    sameColour=0;
                    offset=0;
                    break;
                }
            }
            }
        
        //--------------------------
        
        //it now has to scan NW
        //it can not perform from following locations
        //column 0 and row 0
        System.out.println("DIAGONAL CHECK 4"); 
        
          if (columnChipPlacedZeroIndex!=0 && rowChipPlacedZeroIndex!=0)
          {
              System.out.println("INSIDE D4 - Diagonal north west check");
              //System.out.println(columnChipPlacedZeroIndex);
            
            //it has to scan one column further and one row upward (going toward 0 row index)
            for (int count = rowChipPlacedZeroIndex; count>=0; count--)
            {
                //System.out.println("in row: " + rowChipPlacedZeroIndex);
                //System.out.println("chip: " + String.valueOf(chipValue));
                
                if (board[count][columnChipPlacedZeroIndex-offset].equals(String.valueOf(chipValue)))
                {
                    sameColour++;
                    offset++;
                    //System.out.println("sc:" + sameColour);
                    
                     if (sameColour+runningTotalSameColour==4 && !rightColour)
                    {
                        System.out.println("***9Congratulations " + playerNamechipValue + "  Connect 4 in diagonal");
                        return true;
                    }
                    
                    //need to get an additional since it has counted both left
                    //and right and includes the chip placed in both totals
                    if (sameColour+runningTotalSameColour==5 && rightColour)
                    {
                        System.out.println("***10Congratulations" + playerNamechipValue + "  Connect 4 in diagonal");
                        return true;
                    }
                    
                }
                else
                {
                    rightColour=false;
                    runningTotalSameColour=0;
                    sameColour=0;
                    offset=0;
                    break;
                }
            }
            }
        
        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);
        
        //we want to accept input as a String
        //this will mean that whilst validating input,
        //it needs to keep while loop while 
        //!length==1 && indexOf(numbers between 1 - 7)
        
        viewBoard();
        //currentAvailability();
        
        do
        {
        System.out.println(playerName + "("+chip+")" +  ",  Which column would you like to insert the chip?");
        input = in.next().charAt(0);
        
        String regexColumns="[1234567]";
        Pattern pattern = Pattern.compile(regexColumns, Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(Character.toString(input));
        
        }while (!matcher.find());
        
        //this is to stay inline with zero indexing
        inputAsIntegerZeroIndex = Character.getNumericValue(input-1);
        columnChipPlacedZeroIndex = Character.getNumericValue(input-1);
        columnChipPlaced = Character.getNumericValue(input);
    }
    
    public void viewBoard()
    {
        System.out.println("****CURRENT BOARD****");
        for (String[]row:board)
        {
                System.out.println(Arrays.deepToString(row));
        }
    };
    
    public ConnectFour() 
    {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        board = new String [][]   { {"-", "-", "-", "-", "-", "-", "-"},
                                    {"-", "-", "-", "-", "-", "-", "-"},
                                    {"-", "-", "-", "-", "-", "-", "-"},
                                    {"-", "-", "-", "-", "-", "-", "-"},
                                    {"-", "-", "-", "-", "-", "-", "-"},
                                    {"-", "-", "-", "-", "-", "-", "-"},
                                };
                                
        
        boardHeightZeroIndex = board.length-1;
        
        viewBoard();
        configurePlayers();
        PlayerOne plOne = new PlayerOne (this, playerOneName,playerTwoName);
    }
    
   
   
    
}