#!/usr/bin/env bash
#clear
#export PS4='\e[90m+${LINENO} in ${#BASH_SOURCE[@]}>${FUNCNAME[0]}:${BASH_SOURCE[@]##*/} \e[0m'
#set -x

#echo "starting: $0 <LOG_LEVEL=$1>"

### new.method

ossh.list() {
  ossh.config.get.completion "$@" | line find "$1" | line replace "$1" "${CYAN}$1${NORMAL}"
}

ossh.list.ids() # <?id> # list available ids
{
  tree -L 1 ~/.ssh/ids | line find "$1" 
}

ossh.get.public.id() # <?id> # prints the public key of <id> to the console 
{
  if [ -z "$1" ]; then
    idFile=~/.ssh/id_rsa.pub
    idName=user
  else
    idFile=~/.ssh/ids/"$1"/id_rsa.pub
    idName="$1"
    shift
  fi
  echo $idFile

  cat "$idFile"
}

ossh.id.create.fromKey() # <idName> # creates a new ssh key pair in ~/.ssh/ids/<idName> from a given .ssh-dir with id_rsa key
{
  if [ -z "$1" ]; then
    error.log "no idName"
    return 1
  else
    idName="$1"
    shift
  fi

  if [ -z "$1" ]; then
    error.log "no sshdir"
    return 1
  else
    sshdir="$1"
    shift
  fi

  if [[ ! -f $sshdir/id_rsa || ! -f $sshdir/id_rsa.pub ]]; then
    error.log "mising key $sshdir/id_rsa"
    return 1
  fi

  echo create ~/.ssh/ids/$idName/id_rsa
  mkdir -p ~/.ssh/ids/$idName
  cp $sshdir/id_rsa ~/.ssh/ids/$idName/
  cp $sshdir/id_rsa.pub ~/.ssh/ids/$idName/
  
  mkdir -p ~/.ssh/ids/$idName/public_keys
  mkdir -p ~/.ssh/ids/$idName/private_key

  cp ~/.ssh/ids/$idName/id_rsa ~/.ssh/ids/$idName/private_key/$idName.private_key
  cp ~/.ssh/ids/$idName/id_rsa.pub ~/.ssh/ids/$idName/public_keys/$idName.public_key
}

ossh.id.create() # <idName> # creates a new ssh key pair in ~/.ssh/ids/<idName>
{
  if [ -z "$1" ]; then
    error.log "no idName"
    return 1
  else
    idName="$1"
    shift
  fi

  echo create ~/.ssh/ids/$idName/id_rsa
  mkdir -p ~/.ssh/ids/$idName
  ssh-keygen -f ~/.ssh/ids/$idName/id_rsa
  
  mkdir -p ~/.ssh/ids/$idName/public_keys
  mkdir -p ~/.ssh/ids/$idName/private_key

  cp ~/.ssh/ids/$idName/id_rsa ~/.ssh/ids/$idName/private_key/$idName.private_key
  cp ~/.ssh/ids/$idName/id_rsa.pub ~/.ssh/ids/$idName/public_keys/$idName.public_key
}

ossh.config.create() { # <?sshConfigName> <?url> <?id> # creates a ssh config <?sshConfigName> from <?url> with <?id> file. Without parameter for own ssh config
  #set -x
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    sshConfigHost="$1"
    shift
  else
    local sshConfigHost="$OOSH_SSH_CONFIG_HOST"
    if [ -z "$sshConfigHost" ]; then
      sshConfigHost="$HOSTNAME"
    fi
  fi

  local url="$1"
  if [ -n "$1" ]; then
    url="$1"
    shift
  else
    url="$USER@$(hostname):$( ossh.server.get.port )"
  fi

  local id="$1"
  if [ -n "$1" ]; then
    id="$1"
    shift
  else
    id=~/.ssh/id_rsa
    # id="$CURRENT_SSH_DIR/id_rsa"
  fi

  if [ ! -f $id ]; then
    #warn.log "file \"$id\" does not exist..."
    id='~/.ssh/ids/'$id'/id_rsa'
  fi

  ossh.config.parse.url "$url" #private.config.create
  
}

ossh.create.key.folders() { # # if the .ssh folder was not created with ossh it fixes its structure
  private.get.sshDir "$1"
  if [ -n "$1" ]; then
    shift
  fi
  local sshDir="$RESULT"
  
  mkdir  $sshDir/public_keys                                      
  mkdir $sshDir/private_key
  ossh.get.key.name                                   
  local sshKeyName="$RESULT"
  cp $sshDir/id_rsa $sshDir/private_key/$sshKeyName.private_key
  cp $sshDir/id_rsa.pub $sshDir/public_keys/$sshKeyName.public_key
  ossh.status
}

ossh.delete.key.folders() { # # cleans the .ssh folder to a standard layout....may cause data loss!
  private.get.sshDir "$1"
  if [ -n "$1" ]; then
    shift
  fi
  local sshDir="$RESULT"
  problem.log "are you shure to delete the key folders in: $sshDir"
  rm -rf  $sshDir/public_keys                                      
  rm -rf $sshDir/private_key
  ossh.status
}

ossh.create.and.install() { #  <sshConfigHost> <url> # creates a config <sshConfigName> for <url> and pushes the oosh there 

  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no sshConfigHost was specified"
    return 1
  fi

  local url="$1"
  if [ -n "$1" ]; then
    url="$1"
    shift
  else
    error.log "no url was specified"
    return 1
  fi

  ossh.config.create $sshConfigHost
  ossh.config.save.last
  ossh.config.parse.url $url ossh.install
  
}

ossh.install() { # <sshConfigHost> # pushes the oosh installer to the <sshConfigHost>
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
    ossh.config.parse $sshConfigHost private.push.init.oosh
    return $?
  else
    error.log "no sshConfigHost was specified"
    return 1
  fi
  
  if [ -z "$url" ]; then
    error.log "no url"
    return 1
  fi

  if [ -z "$user" ]; then
    ossh.config.parse.url $url private.push.init.oosh
  else
    private.push.init.oosh
  fi

}

private.push.init.oosh() {

  local file="$HOME/.ssh/public_keys/$( ossh.get.file.name ).public_key"
  info.log "file=$file"

  if scp -o StrictHostKeyChecking=accept-new $OOSH_DIR/init/oosh $file $sshConfigHost:.; then
    success.log "successfully copied oosh"
    #scp $OOSH_DIR/.ssh/oosh $sshConfigHost:/home/$user
    #scp $OOSH_DIR/.ssh/oosh $sshConfigHost:.
  #set -x
    ssh $sshConfigHost "./oosh mode ssh $sshConfigHost $OOSH_SSH_CONFIG_HOST $OOSH_MODE"
  #set -x

    important.log "remote installation done: $?"
  else
    problem.log "maybe already installed?"

    ssh $sshConfigHost "bash /home/$user/oosh/ossh continue.local.install from $(ossh.get.url)"
  fi

  source $CONFIG
  config list
  
  # problem.log "ossh 
  # in private.push.init.oosh
  # before ossh.install.finish.local $sshConfigHost"

  ossh.install.finish.local $sshConfigHost

}

ossh.install.continue.local() { # <remoteSshConfigName> <sshConfigNameUsedForLocal> #
  success.log "remote inititialized over ssh....
  
  here we go: 
  $CONFIG
  $*"
  #set -x 
  source $CONFIG

  local remoteSshConfigName="$1"
  if [ -n "$1" ]; then
    shift
  else
    remoteSshConfigName="unknownRemote"
  fi

  local sshConfigNameUsedForLocal="$1"
  if [ -n "$1" ]; then
    shift
  else
    if [ -n "$OOSH_SSH_CONFIG_HOST" ]; then
      sshConfigNameUsedForLocal="$OOSH_SSH_CONFIG_HOST"
    else
      sshConfigNameUsedForLocal="$HOSTNAME"
    fi
  fi

  oo update
  #source user
  oo state
  state next
  #state next

  source $CONFIG
  config list
  #problem.log "returned from state transition: $?   -$sshConfigNameUsedForLocal"

  config ssh.set.config.host "$sshConfigNameUsedForLocal"

  check dir $HOME/ssh.original not exists \
    call user ssh.backup original
  
  user init

  check dir $HOME/ssh.$USER.$sshConfigNameUsedForLocal.for.$remoteSshConfigName not exists \
    call user ssh.backup $USER.$sshConfigNameUsedForLocal.for.$remoteSshConfigName
  
  mv $HOME/*.public_key $HOME/.ssh/public_keys

  user update.authorized_keys
  #state set 12
  #scp -r $HOME/ssh.original 
  create.result 0 "ossh.install.continue.local done successful" "$1" 
  return $(result)

}

ossh.install.finish.local() {
  important.log "entering ossh.install.finish.local"

  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no sshConfigHost was specified"
    return 1
  fi

  ossh.push.config $sshConfigHost
  remoteKeyName=$( ossh.exec $sshConfigHost "ossh get.key.name" )
  ossh.pull.key $sshConfigHost $remoteKeyName
  ossh.update.authorized_keys
}

ossh.update.authorized_keys()
{

  private.get.sshDir "$1"
  if [ -n "$1" ]; then
    shift
  fi
  local sshDir="$RESULT"

  if ossh.isInstalled; then
    info.log "updating $sshDir/authorized_keys"
    rm $sshDir/authorized_keys
    cat $sshDir/public_keys/* >>~/.ssh/authorized_keys

    chmod 700 $sshDir
    chmod 600 $sshDir/authorized_keys
    chmod 600 $sshDir/id_rsa
  fi
}





ossh.fix.rights() {
  private.get.sshDir "$1"
  if [ -n "$1" ]; then
    shift
  fi
  local sshDir="$RESULT"

  if ossh.isInstalled; then
    chmod 700 $sshDir
    chmod 600 $sshDir/authorized_keys
    chmod +X,go+r $sshDir/*
    chmod 600 $sshDir/id_rsa
    tree -p $sshDir
  fi
}

ossh.get.current.key.file.name() { # # gets the actual file name (instead of the best key name from get.key.name )
  ossh.get.file.name
}

ossh.get.file.name() { # # gets the actual file name (instead of the best key name from get.key.name )
  local sshDir="$1"
  if [ -z "$1" ]; then
    sshDir="$CURRENT_SSH_DIR"
  fi
  if [ -z "$sshDir" ]; then
    sshDir="$HOME/.ssh"
  fi
  shift


  local sshKeyName="$(ls $sshDir/private_key/ | sed 's/\.private_key//')"
  if [ -z "$sshKeyName" ]; then
    error.log "get.file.name: file not found..."
    important.log "recovering by generating key.name for file"
    sshKeyName="$( ossh.get.key.name )"
  fi

  create.result 0 "$sshKeyName" "$1"
  echo "$RESULT"
  return $(result)
}

ossh.show() {
  #sed -n -e '/Host PI/,/Host / p' .ssh/config 
  awk "/Host $1$/,/^$/" .ssh/config 
}

ossh.install.completion() {
  ossh.config.get.completion "$@"
}

ossh.parameter.completion.sshConfigHost() {
    grep '^Host' $HOME/.ssh/config $HOME/.ssh/config.d/* 2>/dev/null | cut -d ' ' -f 2- 
}

ossh.parameter.completion.keyName() {
  ls $HOME/.ssh/public_keys/
}

ossh.parameter.completion.toHost() {
    ossh.parameter.completion.sshConfigHost "$@"
}

ossh.parameter.completion.formHost() {
    ossh.parameter.completion.sshConfigHost "$@"
}

ossh.parameter.completion.sshConfigName() {
    ossh.parameter.completion.sshConfigHost "$@"
}

ossh.parameter.completion.id() {
  ls ~/.ssh/ids
}

ossh.parameter.completion.file() {
  important.log "ossh.parameter.completion.file $*"
  c2 files.completion "$1"
  echo $HOME/.ssh/config
}

ossh.parameter.completion.dir() {
  important.log "ossh.parameter.completion.file $*"
  c2 folders.completion "$1"
  echo $HOME/.ssh/
}

ossh.config.get.completion() {
  ossh.parameter.completion.sshConfigHost "$@"
}

ossh.config.get() { # <sshConfigHost> <?file:$HOME/.ssh/config>   # outputs the ssh config
  local host="$1"
  if [ -n "$1" ]; then
    shift
  else
    ossh.config.create
    #error.log "no host was specified"
    return $?
  fi

  local file="$1"
  if [ -n "$1" ]; then
    shift
  else
    file="$HOME/.ssh/config"
  fi
  echo ""
  cat $file | line.find "Host $host$" "^$" | tee $CONFIG_PATH/result.txt

  if [ -s $CONFIG_PATH/result.txt ]; then
    create.result 0 "config $host found" 
  else

    create.result 2 "config $host not found" 
    warn.log "$RESULT"

  fi


  #awk "/Host $host$/,/^$/" $file 
  #grep -A "5" "Host $host$" .ssh/config 
  return $(result)
}



ossh.config.parse.url() {  # <url>  <?method> # parses the url and shows config values. Then calls optional method that has acces to the paresed values
  local url="$1"
  if [ -n "$1" ]; then
    url="$1"
    shift
  else
    url="$USER@$(hostname):22"
  fi

  local call="$1"
  if [ -n "$1" ]; then
    call="$1"
    shift
  else
    call=""
  fi


  local user="$( echo $url | cut -d"@" -f1  )"
  local hostAndPort="$( echo $url | cut -d"@" -f2  )"

  local hostname="$( echo $hostAndPort | cut -d":" -f1  )"
  local port="$( echo $hostAndPort | cut -d":" -f2  )"
  if ! this.isNumber $port; then
    path=$port
    SSH_CONFIG_Port=22
    port="$SSH_CONFIG_Port"
  fi
  
  if [ -z "$id" ]; then
    local id=~/".ssh/id_rsa"
  fi

  private.config.create
  $call
}

ossh.config.parse() { # <sshConfigHost>  <?method:private.config.create> # parses the config and shows config values. Then calls optional <method> that has acces to the paresed values

  #set -x
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no sshConfigHost was specified"
    return 1
  fi


  local call="$1"
  if [ -n "$1" ]; then
    call="$1"
    shift
  else
    call="private.config.create"
  fi

  ossh.config.get $sshConfigHost | line.trim | line.split | line.unquote | line.format "declare -- SSH_CONFIG_%s=%s\n" >$CONFIG_PATH/result.env
  source $CONFIG_PATH/result.env

  local sshConfigHost="$SSH_CONFIG_Host"
  local user="$SSH_CONFIG_User"
  local hostname="$SSH_CONFIG_HostName"
  
  local path=""
  local port="$SSH_CONFIG_Port"
  if ! this.isNumber port; then
    path=$port
    SSH_CONFIG_Port=22
    port="$SSH_CONFIG_Port"
  fi
  local id="$SSH_CONFIG_IdentityFile"

  $call
}

private.config.create() {
  {
    echo ""
    echo "Host $sshConfigHost"
    echo " User $user"
    echo " Port $port"
    echo " HostName $hostname"
    echo " IdentityFile $id"
    echo ""
  } >$CONFIG_PATH/result.txt
  ossh.config.show.last
}

ossh.get.url() { # <sshConfigHost> # gets the URL for the given <sshConfigHost>
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    echo "$USER@$HOSTNAME"
    #error.log "no sshConfigHost was specified"
    #return 0
  fi
  ossh.config.parse $sshConfigHost private.get.url
}

ossh.get.ssh.parameter() { # <sshConfigHost> # gets the ssh paramerters for the given <sshConfigHost>
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    echo "$USER@$HOSTNAME -p=22"
    return 0
  fi
  ossh.config.parse $sshConfigHost private.get.ssh.parameter
  
}

private.get.url() {
  echo "$SSH_CONFIG_User@$SSH_CONFIG_HostName:$SSH_CONFIG_Port"
}

private.get.ssh.parameter() {
  echo "$SSH_CONFIG_User@$SSH_CONFIG_HostName -p $SSH_CONFIG_Port"
}

private.get.sshDir() {
  local sshDir="$1"
  if [ -z "$1" ]; then
    sshDir="$CURRENT_SSH_DIR"
  fi
  if [ -z "$sshDir" ]; then
    sshDir="$HOME/.ssh"
  fi
  shift

  create.result 0 "$sshDir" "$1"
  echo "$RESULT"
  return $(result)
}

ossh.get.url.completion() {
  ossh.config.get.completion "$@"
}

ossh.get.ssh.parameter.completion() {
  ossh.config.get.completion "$@"
}

ossh.config.parse.completion() {
  ossh.config.get.completion "$@"
}

ossh.get.config.completion() {
  ossh.config.get.completion "$@"
}

ossh.login() { # <sshConfigHost> # like normal ssh connect
  local sshConfigHost="$1"
  if [ -n "$1" ]; then
    sshConfigHost="$1"
    shift
  else
    error.log "no sshConfigHost was specified"
    return 1
  fi

  local sshConfigHostIP=$(grep -A3 "$sshConfigHost" "$HOME/.ssh/config" | grep -A0 HostName | awk '{print $2}')
  ssh -o StrictHostKeyChecking=accept-new "$sshConfigHost"
  # if ssh-keygen -F "$sshConfigHostIP" > /dev/null; then
  #   ssh "$sshConfigHost"
  # else
  #   local loop=true
  #   while [[ $loop == true ]]
  #   do
  #     local answer
  #     # echo "Are you sure you want to continue connecting and add remote fingerprint to known_hosts (Y[es]/N[o])?"
  #     # read -e -i "yes" answer
  #     info.log "Choice to add fingerprint to known_hosts answer: $answer"
  #     answer=$(echo $answer | awk '{print tolower($0)}')

  #     if [ $answer = "yes" -o $answer = "y" ]; then
  #       loop=false
  #       ssh -o StrictHostKeyChecking=accept-new "$sshConfigHost"
  #     elif [ $answer = "no" -o $answer = "n" ]; then
  #       echo "Do you want to use a fingerprint without adding it to known_hosts (Y[es] or abort with N[o])?"
  #       read -e -i "no" answer
  #       info.log "Using fingerpint without adding to known_hosts answer: $answer"
  #       answer=$(echo $answer | awk '{print tolower($0)}')

  #       if [ $answer = "yes" -o $answer = "y" ]; then
          
  #         echo "Give fingerprint"
  #         read -e answer
  #         info.log "Fingerprint answer: $answer"

  #         if [[ "$(ssh-keyscan "$sshConfigHostIP")" =~ "$answer" ]]; then
  #           loop=false
  #           echo "Given fingerprint maches"
  #           ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -q "$sshConfigHost"
  #           info.log "Given fingerprint matches"
  #         else
  #           echo "Given fingerprint doesn't match. Starting from the beginning again"
  #           info.log "Given fingerprint doesn't match. Starting from the beginning again"
  #         fi

  #       elif [ $answer = "no" -o $answer = "n" ]; then
  #         loop=false
  #         echo "Aborting ssh login"
  #         info.log "Aborted ssh login"
  #       else
  #         echo "Unkown response, please try again"
  #         info.log "Restarted ssh login"
  #       fi
  #     else
  #       echo "Unkown response, please try again"
  #       info.log "Restarted ssh login"
  #     fi
  #   done

  # fi
  RETURN=$1
}

ossh.login.completion() {
  ossh.config.get.completion "$@"
}

ossh.get.config() { # <sshConfigHost> <?file:$HOME/.ssh/config> # same as config.get
  ossh.config.get "$@"
}


ossh.get.server.ip() {
  ossh.server.get.ip "$@"
}

ossh.get.server.port() {
  ossh.server.get.port "$@"
}

ossh.config.show.last() {
  cat $CONFIG_PATH/result.txt
}

ossh.config.save.last() {  # <?file:"$HOME/.ssh/config"> # appends the config to file
  local file="$1"
  if [ -n "$1" ]; then
    file="$1"
    shift
  else
    file="$HOME/.ssh/config"
  fi
  cat $CONFIG_PATH/result.txt >>$file
  important.log "appended last config to $file"
}


ossh.config.edit() { # <?file> # opens the config file in an editor
  local file="$1"
  if [ -n "$1" ]; then
    file="$1"
    shift
  else
    file="$HOME/.ssh/config"
  fi
  oo cmd vim
  vim $file
}

ossh.config.list() { # <?file> # prints the config file
  local file="$1"
  if [ -n "$1" ]; then
    file="$1"
    shift
  else
    file="$HOME/.ssh/config"
  fi

  cat $file
}

ossh.status() { # # checks if the users .ssh is configures and if the ssh service is up
  source os
  source oo
  oo.cmd tree
  oo.cmd sshd openssh-server
  if user ssh.status log; then
    tree -p "$HOME/.ssh"
  fi

  if os.check ossh.service.status; then
    $RESULT "$@"
  else
    source $CONFIG_PATH/result.env
    important.log "$RESULT is not supported"
  fi  

}

ossh.isInstalled() # <?log> # returns 1 if there is no ssh init. use log as argument if you want to see the output
{
  #problem.log "isInstalled"
  if [ -f ~/.ssh/id_rsa ]; then
    create.result 0 "ssh is initialized for $USER" "$1"
    if ! [ -d ~/.ssh/public_keys ]; then
      user ssh.create.folders
    fi
  else
    create.result 1 "ssh does not exist for $USER" "$1"
  fi

  if [ "$1" == "log" ]; then
    shift
    echo "$RESULT" 
    RETURN=$1
  else 
    info.log "$RESULT" 
  fi
  return "$(result)"
}

ossh.service.status.unknown() {
  warn.log "could not determine OS....assuming linux"
  ossh.service.status.linux
}

ossh.service.status.linux() {
  if  [ -x "$(command -v service)" ]; then
    if ! service ssh status; then
      service ssh restart
    fi
    return $?
  fi
  if  [ -x "$(command -v systemctl)" ]; then
    if ! systemctl status sshd; then
      systemctl restart sshd
    fi
    return $?
  fi
  sshd &
}

ossh.service.status.darwin() {
  if ! $SUDO systemsetup -getremotelogin; then
    $SUDO launchctl stop com.openssh.sshd
    $SUDO launchctl start com.openssh.sshd
    #sudo systemsetup -setremotelogin on
  fi
}

ossh.config.push() {
  ossh.push.config "$@"
}

ossh.push.config() { # <toHost> <sshConfigName> # adds the config <configName> to the configs on host <toHost>


  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi

  local configName="$1"
  if [ -n "$1" ]; then
    shift
      ossh.get.config $configName >$CONFIG_PATH/tmp.ssh.config.$configName
  else
    ossh.config.create >$CONFIG_PATH/tmp.ssh.config.$configName
  fi


  scp $CONFIG_PATH/tmp.ssh.config.$configName $toHost:config/tmp.ssh.config.$configName
  ssh $toHost "cat config/tmp.ssh.config.$configName >>.ssh/config; rm ~/config/tmp.ssh.config.$configName"
  rm ~/config/tmp.ssh.config.$configName
}

ossh.push.config.completion() {
  ossh.config.get.completion "$@"
}


ossh.config.push.completion() {
  ossh.config.get.completion "$@"
}

ossh.push.key() { #  <toHost> <?keyName> # adds your key to host <toHost> optional you can specify another <?keyName> form $HOME/.ssh/public_keys/.
  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi

  local keyName="$1"
  if [ -n "$1" ]; then
    shift
  else
    keyName="$( ossh.get.file.name ).public_key"
  fi

  echo $HOME/.ssh/public_keys/$keyName $toHost:.ssh/public_keys/$keyName
  scp $HOME/.ssh/public_keys/$keyName $toHost:.ssh/public_keys/$keyName
  ossh.exec $toHost "user update.authorized_keys"
  #ssh $toHost "user update.authorized_keys"
}

ossh.get.key.name() { # # creates a good key name fot this user and host
  local sshKeyName="$SSH_KEY_NAME"

  # if [ -z "$SSH_KEY_NAME" ]; then
  #   source config
  #   config.load ssh.info
  # fi

  if [ -z "$sshKeyName" ]; then
    if [ -n "$OOSH_SSH_CONFIG_HOST" ]; then
      ossh.set.key.name "$USER.$OOSH_SSH_CONFIG_HOST"
    else
      ossh.set.key.name "$USER.$(hostname -f)"
    fi
  fi

  create.result 0 "$RESULT" "$1"
  echo "$RESULT"
  return $(result)
}


ossh.set.key.name() {  # <?sshKeyName> # sets the key name to sshKeyName and saves it in $CONFIG_PATH/ssh.info.env
  local sshKeyName="$1"
  if [ -z "$sshKeyName" ]; then
    export SSH_KEY_NAME="$USER.$(hostname -f)"
  fi
  config save "ssh.info" "SSH_" >/dev/null
  stop.log "$SSH_KEY_NAME - $sshKeyName"
  create.result 0 "$sshKeyName" "$1"
  return $(result)
}

# ossh.get.file.name() { # # gets the actual key file name (instead of the best key name from get.key.name )
#   local sshDir="$1"
#   if [ -z "$1" ]; then
#     sshDir="$CURRENT_SSH_DIR"
#   fi
#   if [ -z "$sshDir" ]; then
#     sshDir="$HOME/.ssh"
#   fi
#   shift


#   local sshKeyName="$(ls $sshDir/private_key/ | sed 's/\.private_key//')"

#   create.result 0 "$sshKeyName" "$1"
#   echo "$RESULT"
#   return $(result)
# }


ossh.push.dir() { # <toHost> <dir> # adds the config <configName> to the configs on host <toHost>

  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi

  local dir="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no dir was specified"
    return 1
  fi

  scp -r $dir $toHost:.
}


ossh.push.dir.completion() {
  ossh.config.get.completion "$@"
}


ossh.push.key.completion() {
  #ls $HOME/.ssh/public_keys/$1*
    ossh.config.get.completion "$@"
}

ossh.pull.dir() { # <fromHost> <dir> # gets te directory <dir> <fromHost> into the CURRENT LOCAL dirrectory

  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi

  local dir="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no dir was specified"
    return 1
  fi

  important.log "copying from $toHost:$dir"
  scp -r $toHost:$dir .
  #ssh $toHost "user update.authorized_keys"
}

ossh.pull.key() { # <fromHost> <dir> # gets te directory <dir> <fromHost> into the CURRENT LOCAL dirrectory

  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi

  local keyName="$1"
  if [ -n "$1" ]; then
    shift
  else
    keyName=$( ossh exec $toHost "ossh get.file.name" )
    error.log "no dir was specified: using $keyName"
    #return 1
  fi

  scp -r $toHost:.ssh/public_keys/$keyName.public_key $HOME/.ssh/public_keys/$keyName.public_key.pulled
  #ssh $toHost "user update.authorized_keys"
}

ossh.pull.config() { # <fromHost> <sshConfigName> <?file> # pulls a config from remote and seves it
  local fromHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no fromHost was specified"
    return 1
  fi

  local sshConfigName="$1"
  if [ -n "$1" ]; then
    shift
  else

    error.log "no sshConfigName was specified."
    important.log "available remote ssh configs:"
    ossh exec $fromHost "ossh config.list"
    return 1
  fi

  local file="$1"
  if [ -n "$1" ]; then
    file="$1"
    shift
  else
    file="$HOME/.ssh/config"
  fi

  ossh exec $fromHost "ossh get.config $sshConfigName" >>$file
}

ossh.pull.id() # <fromHost> <?file> # pulls the .ssh dir <formHost> to .ssh/ids and renames it
# TODO get remote user and rename to ssh.$remoteUser.$fromHost
{
  local fromHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no fromHost was specified"
    return 1
  fi

  local id=$( ossh exec $fromHost "ossh get.key.name" )

  local file="$1"
  if [ -n "$1" ]; then
    file="$1"
    shift
  else
    file="$HOME/.ssh/config"
  fi

  if ! [ -d $HOME/.ssh/ids ]; then
    mkdir -p $HOME/.ssh/ids
  fi
  
  cd $HOME/.ssh/ids
  ossh pull.dir $fromHost ".ssh" 
  mv .ssh ssh.$id
}

ossh.push.id() # <toHost> <id> # puches the <id> <toHost> 
{
  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi

  if [ -z "$1" ]; then
    error.log "no id was specified"
    return 1
  else
    idDir=~/.ssh/ids/"$1"
    idName="$1"
    shift
  fi

  scp -r $idDir $toHost:~/.ssh/ids/
}

ossh.get.server.port() {
  if [ -f /etc/ssh/sshd_config ]; then
    cat /etc/ssh/sshd_config | line find 'Port ' | line split | line select 2
  else
    echo 22
  fi
}

ossh.server.get.port() {
  ossh.get.server.port
}

ossh.server.get.ip() {
  if os.check ossh.server.get.ip; then
    $RESULT "$@"
  else
    important.log "$RESULT is not supported"
  fi  
}

ossh.server.get.ip.darwin() {
  ifconfig | line find 192 | line split | line select 2
}

ossh.server.get.ip.linux() {
  hostname -I
}

ossh.server.config.edit() {
  vim /etc/ssh/sshd_config 
}

ossh.tunnel() # <toHost> <portMap:> # maps the ports on localhost <toHost>
# eg ossh tunnel someHost  8093:127.0.0.1:8080
{
  ssh $1 -L $2
}

ossh.list.open.ports() { # # lists open ports an their applications and users
  lsof -Pn -i4
}

ossh.exec() { # <toHost> <command> # executes the command on host <toHost>

  local toHost="$1"
  if [ -n "$1" ]; then
    shift
  else
    error.log "no toHost was specified"
    return 1
  fi


  ssh $toHost "source config/user.env; $@"
}


ossh.exec.completion() {
  ossh.config.get.completion "$@"
}

ossh.test() {
  this.isSourced
  echo this.isSourced=$?
  echo "this: $this"
  echo "caller: $caller"
  echo "callerFunction: $callerFunction"
  echo "This: $This"
  #echo "this.this: $(this.this)"
}

ossh.usage()
{
  local this=${0##*/}
  echo "You started" 
  echo "$0

  Usage:
  $this: command   description and Parameter

      usage     prints this dialog while it will print the status when there are no parameters          
      v         print version information
      init      initializes ...nothing yet

      config.get          <sshConfigHost> <?file:\"$HOME/.ssh/config\">   outputs the ssh config

      config.create       <sshConfigHost> <?url> <privateKey:\"$HOME/.ssh/id_rsa\">
      config.show.last    shows \$CONFIG_PATH: $CONFIG_PATH/result.txt
      config.save.last    <?file:\"$HOME/.ssh/config\">

      config.parse.url    <?url> parses a url and shows a resulting config

      install             <?url> installs password free access to the user in the url 

      ----      --------------------------"
  this.help
  echo "${GREEN}
  
  Examples

    $this config.create iMac
    $this config.create dockerSSH test@localhost:8022
    $this config.show.last
    $this config.save.last

  "
}

ossh.start()
{
  #echo "sourcing init"
  if [ -z "$CONFIG" ]; then
    CONFIG=$HOME/config/user.env
    source $CONFIG
  fi
  source this
  source line
  source os

  # if [ -z "$1" ]; then
  #   status.discover "$@"
  #   return 0
  # fi

  this.start "$@"
}

ossh.start "$@"

