<commit_before>/**
* Since we use a single file named [...params] to aggregate
* all of the configurable options passeed into the url, we have
* to parse the parameters positional based on their position in the
* URL.
*
* The expected URL structire is /{provider}/{org}/{repo}/{ref}/{filepath}.
*/

import React, { FC,  HTMLAttributes, useState, useEffect} from "react";
import { WithRouterProps } from "next/dist/client/with-router";
import { withRouter } from "next/router";
import { Octokit } from "@octokit/rest";

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlay, faPlus, faSave, faBars, faTerminal, faServer} from '@fortawesome/free-solid-svg-icons'
import { faGithubAlt,  faPython } from '@fortawesome/free-brands-svg-icons'

import Notebook from "@nteract/stateful-components";
import dynamic from "next/dynamic";
import Head from "next/head";
import { Host } from "@mybinder/host-cache";

const CodeMirrorEditor = dynamic(() => import('@nteract/editor'), { ssr: false });

import { Menu, MenuItem } from '../../components/Menu'
import { Button } from '../../components/Button'
import { Console } from '../../components/Console'
import { BinderMenu } from '../../components/BinderMenu'
import { Avatar } from '../../components/Avatar'
import { Input } from '../../components/Input'
import { Dialog, Shadow, DialogRow, DialogFooter } from '../../components/Dialog';
import { FilesListing } from "../../components/FilesListing"
import { Layout, Header, Body, Side, Footer} from "../../components/Layout"

const runIcon =  <FontAwesomeIcon icon={faPlay} />
const saveIcon =  <FontAwesomeIcon icon={faSave} />
const menuIcon =  <FontAwesomeIcon icon={faBars} />
const githubIcon =  <FontAwesomeIcon icon={faGithubAlt} />
const consoleIcon =  <FontAwesomeIcon icon={faTerminal} />
const pythonIcon =  <FontAwesomeIcon icon={faPython} />
const serverIcon =  <FontAwesomeIcon icon={faServer} />
const commitIcon =  <FontAwesomeIcon icon={faPlus} />

const Binder = dynamic(() => import("../../components/Binder"), {
  ssr: false
});

const BINDER_URL = "https://mybinder.org";

/* TODO: Functions like this are or can be used in 
 multiple files, find a way to make them globally available. */
function getPath(params){
    const filepathSegments = params.slice(4);
    let filepath;
    if (typeof filepathSegments !== "string") {
      filepath = filepathSegments.join("");
    } else {
      filepath = filepathSegments;
    }
  
    return filepath
  }

// TODO: Below two functions are identical, they can be one
function useInput(val: string | undefined ){
  const [value, setValue] = useState(val);
  
  function handleChange(e: React.FormEvent<HTMLInputElement> | React.FormEvent<HTMLSelectElement>) {
    setValue(e.currentTarget.value);
  }
  
  return { 
    value, 
    onChange: handleChange
  }
}

function useCheckInput(val: boolean | undefined ){
  const [value, setValue] = useState(val);
  
  function handleChange(e: React.FormEvent<HTMLInputElement>) {
    setValue(e.currentTarget.checked);
  }
  
  return { 
    value, 
    onChange: handleChange
  }
}


export interface Props extends HTMLAttributes<HTMLDivElement> {
  router: any
}

export const Main: FC<WithRouterProps> = (props: Props) => {

    const { params } = props.router.query;
    // Toggle Values
    const [ showBinderMenu, setShowBinderMenu ] = useState(false)
    const [ showConsole, setShowConsole ] = useState(false)
    const [ showSaveDialog, setShowSaveDialog ] = useState(false)
   
    // Git API Values
    /* TODO: We need to be able to save multiple files, so this logic 
       won't work. We need to have store for multiple files */
    const [ filepath, setFilepath ] = useState(getPath(params))
    const [ fileContent, setFileContent ] = useState("")
    const [ fileType, setFileType ] = useState("")
    const [ provider, setProvider ] = useState(params[0])
    const [ org, setOrg ] = useState(params[1])
    const [ repo, setRepo ] = useState(params[2])
    const [ gitRef, setGitRef ] = useState(params[3])
  
    // Commit Values
    const commitMessage = useInput("")
    const commitDescription = useInput("")
    // This should be a boolean value but as a string
    const stripOutput = useCheckInput(false)
    const [ fileBuffer, setFileBuffer ] = useState({})
    
    // Login Values
    const [ loggedIn, setLoggedIn ] = useState(false)
    const [ username, setUsername ] = useState("")
    const [ userImage, setUserImage ] = useState("")
    const [ userLink, setUserLink ] = useState("")

/* 
* TODO: Add @nteract/mythic-notifications to file
*/

// This Effect runs only when the username change
useEffect( () => {
  // Check if user has a token saved
  if ( localStorage.getItem("token") != undefined ){
        getGithubUserDetails()
  }
}, [username])

function addBuffer(e){
  setFileContent(e);
  const newFileBuffer = fileBuffer
  newFileBuffer[filepath] = e
  setFileBuffer(newFileBuffer)
  console.table(fileBuffer)
}

 function toggle( value, setFunction){
   setFunction(!value)
  }

  function run(){
    console.log("run binder here")
  }

  function showSave(){
    toggle(showSaveDialog, setShowSaveDialog)
  }

  function isNotebook (name: string) {
    return name.includes(".ipynb") 
  }

  function onSave(event){
  event.preventDefault()
  
  if( username == org ){
        console.log("user is owner")
  }else{
    let forkFound = false;
    listForks(org, repo).then( ({data}) => {
     for (const repo of data){
        if ( repo.owner.login == username){
          forkFound = true
          break;
        }
     };

     if (!forkFound)
        createFork(org, repo)
    })
  }

    // TODO: Add a blob here with changes made and push them to github

  }

  function listForks(owner, repo){
   return new Promise(function(resolve, reject) { <commit_msg>Save/upload to github<commit_after>/**
* Since we use a single file named [...params] to aggregate
* all of the configurable options passeed into the url, we have
* to parse the parameters positional based on their position in the
* URL.
*
* The expected URL structire is /{provider}/{org}/{repo}/{ref}/{filepath}.
*/

import React, { FC,  HTMLAttributes, useState, useEffect} from "react";
import { WithRouterProps } from "next/dist/client/with-router";
import { withRouter } from "next/router";
import { Octokit } from "@octokit/rest";

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlay, faPlus, faSave, faBars, faTerminal, faServer} from '@fortawesome/free-solid-svg-icons'
import { faGithubAlt,  faPython } from '@fortawesome/free-brands-svg-icons'

import Notebook from "@nteract/stateful-components";
import dynamic from "next/dynamic";
import Head from "next/head";
import { Host } from "@mybinder/host-cache";

const CodeMirrorEditor = dynamic(() => import('@nteract/editor'), { ssr: false });

import { Menu, MenuItem } from '../../components/Menu'
import { Button } from '../../components/Button'
import { Console } from '../../components/Console'
import { BinderMenu } from '../../components/BinderMenu'
import { Avatar } from '../../components/Avatar'
import { Input } from '../../components/Input'
import { Dialog, Shadow, DialogRow, DialogFooter } from '../../components/Dialog';
import { FilesListing } from "../../components/FilesListing"
import { Layout, Header, Body, Side, Footer} from "../../components/Layout"

const runIcon =  <FontAwesomeIcon icon={faPlay} />
const saveIcon =  <FontAwesomeIcon icon={faSave} />
const menuIcon =  <FontAwesomeIcon icon={faBars} />
const githubIcon =  <FontAwesomeIcon icon={faGithubAlt} />
const consoleIcon =  <FontAwesomeIcon icon={faTerminal} />
const pythonIcon =  <FontAwesomeIcon icon={faPython} />
const serverIcon =  <FontAwesomeIcon icon={faServer} />
const commitIcon =  <FontAwesomeIcon icon={faPlus} />

const Binder = dynamic(() => import("../../components/Binder"), {
  ssr: false
});

const BINDER_URL = "https://mybinder.org";

/* TODO: Functions like this are or can be used in 
 multiple files, find a way to make them globally available. */
function getPath(params){
    const filepathSegments = params.slice(4);
    let filepath;
    if (typeof filepathSegments !== "string") {
      filepath = filepathSegments.join("");
    } else {
      filepath = filepathSegments;
    }
  
    return filepath
  }

// TODO: Below two functions are identical, they can be one
function useInput(val: string | undefined ){
  const [value, setValue] = useState(val);
  
  function handleChange(e: React.FormEvent<HTMLInputElement> | React.FormEvent<HTMLSelectElement>) {
    setValue(e.currentTarget.value);
  }
  
  return { 
    value, 
    onChange: handleChange
  }
}

function useCheckInput(val: boolean | undefined ){
  const [value, setValue] = useState(val);
  
  function handleChange(e: React.FormEvent<HTMLInputElement>) {
    setValue(e.currentTarget.checked);
  }
  
  return { 
    value, 
    onChange: handleChange
  }
}

/*
 *
 * Save commit to github
 *
 */
const uploadToRepo = async (
    octo: Octokit,
    org: string,
    repo: string,
    branch: string = `master`,
    buffer: {},
) => {
    console.log("inside uploadToRepo")
    const currentCommit = await getCurrentCommit(octo, org, repo, branch)
    console.log(currentCommit)
    // Save only if buffer is not empty
    let pathsForBlobs = []
    let filesContent = []
    for( var key in buffer){
      pathsForBlobs.push(key)
      filesContent.push(buffer[key])
    }
    const filesBlobs = await Promise.all(filesContent.map(createBlobForFile(octo, org, repo)))
    console.log(filesBlobs) 

    //const pathsForBlobs = filesPaths.map(fullPath => path.relative(coursePath, fullPath))
    const newTree = await createNewTree(
          octo,
          org,
          repo,
          filesBlobs,
          pathsForBlobs,
          currentCommit.treeSha
        )
    console.log(newTree)
    const commitMessage = `Auto Commit`
    const newCommit = await createNewCommit(
          octo,
          org,
          repo,
          commitMessage,
          newTree.sha,
          currentCommit.commitSha
        )
     console.log(newCommit)
    await setBranchToCommit(octo, org, repo, branch, newCommit.sha)
}

const getCurrentCommit = async (
    octo: Octokit,
    org: string,
    repo: string,
    branch: string = 'master'
) => {
    const { data: refData } = await octo.git.getRef({
          owner: org,
          repo,
          ref: `heads/${branch}`,
            })
    const commitSha = refData.object.sha
    const { data: commitData } = await octo.git.getCommit({
          owner: org,
          repo,
          commit_sha: commitSha,
        })
    return {
          commitSha,
          treeSha: commitData.tree.sha,
        }
}

// Notice that readFile's utf8 is typed differently from Github's utf-8
//const getFileAsUTF8 = (filePath: string) => readFile(filePath, 'utf8')

const createBlobForFile = (octo: Octokit, org: string, repo: string) => async (
    content: string
) => {
    const blobData = await octo.git.createBlob({
          owner: org,
          repo,
          content,
          encoding: 'utf-8',
        })
    return blobData.data
}

const createNewTree = async (
    octo: Octokit,
    owner: string,
    repo: string,
    blobs: Octokit.GitCreateBlobResponse[],
    paths: string[],
    parentTreeSha: string
) => {
  const tree = blobs.map(({ sha }, index) => ({
        path: paths[index],
        mode: `100644`,
        type: `blob`,
        sha,
      })) as Octokit.GitCreateTreeParamsTree[]
    const { data } = await octo.git.createTree({
          owner,
          repo,
          tree,
          base_tree: parentTreeSha,
        })
    return data
}

const createNewCommit = async (
    octo: Octokit,
    org: string,
    repo: string,
    message: string,
    currentTreeSha: string,
    currentCommitSha: string
) =>
    (await octo.git.createCommit({
          owner: org,
          repo,
          message,
          tree: currentTreeSha,
          parents: [currentCommitSha],
        })).data

const setBranchToCommit = (
    octo: Octokit,
    org: string,
    repo: string,
    branch: string = `master`,
    commitSha: string
) =>
    octo.git.updateRef({
          owner: org,
          repo,
          ref: `heads/${branch}`,
          sha: commitSha,
    })

export interface Props extends HTMLAttributes<HTMLDivElement> {
  router: any
}

export const Main: FC<WithRouterProps> = (props: Props) => {

    const { params } = props.router.query;
    // Toggle Values
    const [ showBinderMenu, setShowBinderMenu ] = useState(false)
    const [ showConsole, setShowConsole ] = useState(false)
    const [ showSaveDialog, setShowSaveDialog ] = useState(false)
   
    // Git API Values
    /* TODO: We need to be able to save multiple files, so this logic 
       won't work. We need to have store for multiple files */
    const [ filepath, setFilepath ] = useState(getPath(params))
    const [ fileContent, setFileContent ] = useState("")
    const [ fileType, setFileType ] = useState("")
    const [ provider, setProvider ] = useState(params[0])
    const [ org, setOrg ] = useState(params[1])
    const [ repo, setRepo ] = useState(params[2])
    const [ gitRef, setGitRef ] = useState(params[3])
  
    // Commit Values
    const commitMessage = useInput("")
    const commitDescription = useInput("")
    // This should be a boolean value but as a string
    const stripOutput = useCheckInput(false)
    const [ fileBuffer, setFileBuffer ] = useState({})
    
    // Login Values
    const [ loggedIn, setLoggedIn ] = useState(false)
    const [ username, setUsername ] = useState("")
    const [ userImage, setUserImage ] = useState("")
    const [ userLink, setUserLink ] = useState("")

/* 
* TODO: Add @nteract/mythic-notifications to file
*/

// This Effect runs only when the username change
useEffect( () => {
  // Check if user has a token saved
  if ( localStorage.getItem("token") != undefined ){
        getGithubUserDetails()
  }
}, [username])

function addBuffer(e){
  setFileContent(e);
  const newFileBuffer = fileBuffer
  newFileBuffer[filepath] = e
  setFileBuffer(newFileBuffer)
}

 function toggle( value, setFunction){
   setFunction(!value)
  }

  function run(){
    console.log("run binder here")
  }

  function showSave(){
    toggle(showSaveDialog, setShowSaveDialog)
  }

  function isNotebook (name: string) {
    return name.includes(".ipynb") 
  }

  function onSave(event){
  event.preventDefault()
    
  if( username == org ){
        console.log("user is owner")
  }else{
    let forkFound = false;
    listForks(org, repo).then( ({data}) => {
     for (const repo of data){
        if ( repo.owner.login == username){
          forkFound = true
          break;
        }
     };

     if (!forkFound)
        createFork(org, repo)
    })
  }
  // Change org to username as the fork exist
  setOrg(username)


   const auth = localStorage.getItem("token") 
   const octo = new Octokit({
     auth
   })
    
    // TODO: Add a blob here with changes made and push them to github
    uploadToRepo( octo, org, repo, gitRef, fileBuffer)
  }

  function listForks(owner, repo){
   return new Promise(function(resolve, reject) { 
{'fraction_non_alphanumeric': 0.12577089620784673, 'fraction_numerical': 0.0013777719459388531, 'mean_word_length': 3.0887875536480687, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 49, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1676055', 'n_tokens_mistral': 4657, 'n_tokens_neox': 4644, 'n_words': 1278}