/* nwc_path.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2006-2021 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "nwc_path.hh"
#include "nwc_os.hh"
#include "aguix/lowlevelfunc.h"
#include "aguix/util.h"

namespace NWC {
    namespace Path {
    
        std::string join( const std::string &p1, const std::string &p2 )
        {
            std::string res;
            res = p1;

            int l = res.length();
            if ( l > 0 ) {
                if ( res[l - 1] != '/' ) {
                    res += "/";
                }
            }

            if ( ! p2.empty() &&
                 p2[0] == '/' ) {
                res = p2;
            } else {
                res += p2;
            }

            return res;
        }

        std::string normalize( const std::string &path )
        {
            //TODO this could be done better
            // perhaps just splitting the string on slash into array
            // then push each directoy component onto new array unless .. appears
            // which removes the last component again
            // after that a new path string is created
            char *tstr = NWC::Path::handlePath( path.c_str() );
            if ( tstr != NULL ) {
                std::string res = tstr;
                _freesafe( tstr );
                return res;
            }
            return "";
        }

        std::string basename( const std::string &path )
        {
            std::string::size_type pos = path.rfind( "/" );
            if ( pos == std::string::npos )
                return path;
          
            return std::string( path, pos + 1 );
        }

        std::string dirname( const std::string &path )
        {
            std::string::size_type pos = path.rfind( "/" );
            if ( pos == std::string::npos ) {
                return std::string( "" );
            }
          
            if ( pos == 0 ) pos++;

            return std::string( path, 0, pos );
        }

        bool isAbs( const std::string &path )
        {
            if ( path.length() < 1 ) return false;
            if ( path[0] != '/' ) return false;
            return true;
        }

        char *parentDir(const char *pathstr,char *dirnamereturn)
        {
            char *newpath;
            int x,y;
            x=strlen(pathstr);
            if(x<2) {
                newpath=(char*)_allocsafe(2);
                strcpy(newpath,"/");
                if(dirnamereturn!=NULL) strcpy(dirnamereturn,"");
            } else {
                if(pathstr[x-1]=='/') y=x-2; else y=x-1;
                while((pathstr[y]!='/')&&(y>0)) {
                    y--;
                }
                if(y==0) {
                    newpath=(char*)_allocsafe(2);
                    strcpy(newpath,"/");
                    if(dirnamereturn!=NULL) strcpy(dirnamereturn,pathstr+1);
                } else {
                    newpath=(char*)_allocsafe(y+1);
                    strncpy(newpath,pathstr,y);
                    if(dirnamereturn!=NULL) strcpy(dirnamereturn,pathstr+y+1);
                    newpath[y]=0;
                }
            }
            return newpath;
        }

        char *handlePath(const char *pathstr)
        {
            char *newpath, *newpath2, *buffer;
            int x,y;
            int start,end;
            int ende;
  
            buffer = (char*)_allocsafe( strlen( pathstr ) + 1 );
            x=strlen(pathstr);
            y=0;
            ende=0;
            newpath=(char*)_allocsafe(1);
            newpath[0]=0;
            do {
                start=y;
                while ( pathstr[y] != '/' && pathstr[y] != '\0' ) {
                    y++;
                }
                end = y;
                if(end>start) {
                    strncpy(buffer,pathstr+start,end-start);
                    buffer[end-start]=0;
                    if(strcmp(buffer,".")==0) {
                        y++;
                    } else if(strcmp(buffer,"..")==0) {
                        if ( strchr( newpath, '/' )  == NULL ) {
                            // no previous dir so keep ".."
                            newpath2 = (char*)_allocsafe( strlen( newpath ) + 3 + 1);
                            if ( ( strlen( newpath ) > 0 ) && ( newpath[ strlen( newpath ) - 1 ] != '/' ) ) {
                                sprintf( newpath2, "%s/..", newpath );
                            } else {
                                sprintf( newpath2, "%s..", newpath );
                            }
                            _freesafe( newpath );
                            newpath = newpath2;
                        } else {
                            newpath2=NWC::Path::parentDir(newpath,NULL);
                            _freesafe(newpath);
                            newpath=newpath2;
                        }
                        y++;
                    } else {
                        newpath2=(char*)_allocsafe(strlen(newpath)+1+strlen(buffer)+1);
                        strcpy(newpath2,newpath);
                        if(strlen(newpath)>0) {
                            if(newpath[strlen(newpath)-1]!='/') strcat(newpath2,"/");
                        }
                        strcat(newpath2,buffer);
                        _freesafe(newpath);
                        newpath=newpath2;
                        y++;
                    }
                } else {
                    if(start==0) {
                        newpath2=(char*)_allocsafe(2);
                        strcpy(newpath2,"/");
                        _freesafe(newpath);
                        newpath=newpath2;
                    }
                    y++;
                }
                if(y>=x) ende=1;
            } while(ende==0);

            _freesafe( buffer );
            return newpath;
        }

        char *handlePathExt(const char *src)
        {
            char *tstr,*tstr2;
            char *newpath;
            char *buf;
            int i,j;

            newpath=dupstring("");
            int pos=0,count;

            // resolve "."
            if ( src[0] == '.' ) {
                if ( src[1] == 0 || src[1] == '/' ) {
                    std::string cwd = NWC::OS::getCWD();
                    _freesafe( newpath );
                    if ( src[1] == '/' ) newpath = catstring( cwd.c_str(), "/" );
                    else newpath = dupstring( cwd.c_str() );
                    pos = 2;
                }
            }
            for(i=pos;i<(int)strlen(src);i++) {
                if(src[i]=='$') {
                    // there is an env-variable to resolve
                    if(i>pos) {
                        // copy the chars before
                        buf=(char*)_allocsafe(i-pos+1);
                        strncpy(buf,src+pos,i-pos);
                        buf[i-pos]=0;
                        tstr=catstring(newpath,buf);
                        _freesafe(newpath);
                        _freesafe(buf);
                        newpath=tstr;
                    }
                    // now find the end of the env-variable
                    if(src[i+1]=='{') {
                        count=1;
                        // search for closing bracket
                        for(j=i+2;;j++) {
                            if(src[j]=='{') count++;
                            else if(src[j]==0) break;
                            else if(src[j]=='}') count--;
                            if(count==0) {
                                j++; // to point at the first char after this
                                break;
                            }
                        }
                        // j-1 is the closing bracket
                        if((j-2)>=(i+2)) {
                            tstr=dupstring(src+i+2);
                            tstr[(j-2)-(i+2)+1]=0;
                            tstr2=NWC::OS::resolveEnv(tstr);
                            _freesafe(tstr);
                            tstr=catstring(newpath,tstr2);
                            _freesafe(newpath);
                            _freesafe(tstr2);
                            newpath=tstr;
                        }
                    } else {
                        // find the "/" or the end
                        for(j=i+1;;j++) {
                            if(src[j]==0) break;
                            else if(src[j]=='/') break;
                        }
                        // j-1 is last char for the env
                        if((j-1)>=(i+1)) {
                            tstr=dupstring(src+i+1);
                            tstr[(j-1)-(i+1)+1]=0;
                            tstr2=NWC::OS::resolveEnv(tstr);
                            _freesafe(tstr);
                            tstr=catstring(newpath,tstr2);
                            _freesafe(newpath);
                            _freesafe(tstr2);
                            newpath=tstr;
                        }
                    }
                    pos=j;
                    i=pos;
                }
            }
            if(i>pos) {
                // copy the chars before
                buf=(char*)_allocsafe(i-pos+1);
                strncpy(buf,src+pos,i-pos);
                buf[i-pos]=0;
                tstr=catstring(newpath,buf);
                _freesafe(newpath);
                _freesafe(buf);
                newpath=tstr;
            }
            // now resolve the normal things like ".." "." and so on
            tstr=NWC::Path::handlePath(newpath);
            _freesafe(newpath);
            return tstr;
        }

        char *parentDir(const char *pathstr,char *dirnamereturn,int returnsize)
        {
            char *newpath;
            int x,y;
            x=strlen(pathstr);
            if(x<2) {
                newpath=(char*)_allocsafe(2);
                strcpy(newpath,"/");
                if(dirnamereturn!=NULL) strcpy(dirnamereturn,"");
            } else {
                if(pathstr[x-1]=='/') y=x-2; else y=x-1;
                while((pathstr[y]!='/')&&(y>0)) {
                    y--;
                }
                if(y==0) {
                    newpath=(char*)_allocsafe(2);
                    strcpy(newpath,"/");
                    if(dirnamereturn!=NULL) {
                        strncpy(dirnamereturn,pathstr+1,returnsize);
                        dirnamereturn[returnsize-1]=0;
                    }
                } else {
                    newpath=(char*)_allocsafe(y+1);
                    strncpy(newpath,pathstr,y);
                    if(dirnamereturn!=NULL) {
                        strncpy(dirnamereturn,pathstr+y+1,returnsize);
                        dirnamereturn[returnsize-1]=0;
                    }
                    newpath[y]=0;
                }
            }
            return newpath;
        }

        std::string get_extended_basename( const std::string &prefix,
                                           const std::string &fullname )
        {
            if ( prefix.length() >= fullname.length() ) return "";

            // fullname is at least one char longer

            // no prefix?
            if ( ! AGUIXUtils::starts_with( fullname, prefix ) ) return "";

            std::string res;

            if ( AGUIXUtils::ends_with( prefix, "/" ) ) {
                // prefix already ends with slash no do not remove another byte
                res = std::string( fullname, prefix.length() );
            } else {
                // there must be a slash after the prefix so it is really a sub-entry
                if ( fullname[ prefix.length() ] != '/' ) return "";

                // no slash at end of prefix
                res = std::string( fullname, prefix.length() + 1 );
            }

            // if result still starts with a slash, there was a problem like
            // an unnormalized path
            if ( AGUIXUtils::starts_with( res, "/" ) ) return "";

            return res;
        }

        bool is_prefix_dir( const std::string &prefix,
                            const std::string &path )
        {
            if ( prefix.length() > path.length() ) return false;

            if ( prefix == path ) return true;

            if ( ! AGUIXUtils::starts_with( path, prefix ) ) return false;

            // check if prefix ends with a slash in which case nothing
            // else needs to be checked
            if ( AGUIXUtils::ends_with( prefix, "/" ) ) {
                return true;
            }

            // if path continues with anything other than a slash, the
            // prefix won't match
            if ( path[ prefix.length() ] != '/' ) return false;

            return true;
        }

        std::string get_real_path( const std::string &path )
        {
            char *rp = realpath( path.c_str(), NULL );

            if ( ! rp ) return "";

            std::string res = rp;

            free( rp );
            
            return res;
        }

        std::string get_last_local_path( const std::string &path )
        {
            if ( path.empty() ) return path;

            if ( worker_islocal( path.c_str() ) == 1 ) {
                return path;
            }

            return get_last_local_path( dirname( path ) );
        }
    }
}
